项目场景:

特殊场景需要写代码解析url的请求参数

问题描述:

网上查了一下, 大多数的工具类是使用&号分割, 然后再使用=号分割得到参数的key和value

这种写法有问题: 当value包含&或者=符号都会产生分割不准确的问题

而且单纯的分隔不会解码%22这种url编码

我们来分析一下tomcat是怎么解析的

这里说的参数是GET请求?号后面的参数 和 POST下application/x-www-form-urlencoded类型的参数解析

源码分析:

Tomcat环境中参数解析的方法调用栈

org.apache.catalina.connector.Request#getParameterMap

org.apache.catalina.connector.Request#getParameterNames

org.apache.tomcat.util.http.Parameters#getParameterNames

org.apache.tomcat.util.http.Parameters#handleQueryParameters

org.apache.tomcat.util.http.Parameters#processParameters(org.apache.tomcat.util.buf.MessageBytes, java.nio.charset.Charset)

org.apache.tomcat.util.http.Parameters#processParameters(byte[], int, int, java.nio.charset.Charset)

    private void processParameters(byte bytes[], int start, int len, Charset charset) {if(log.isDebugEnabled()) {log.debug(sm.getString("parameters.bytes",new String(bytes, start, len, DEFAULT_BODY_CHARSET)));}int decodeFailCount = 0;int pos = start;int end = start + len;while(pos < end) {int nameStart = pos;int nameEnd = -1;int valueStart = -1;int valueEnd = -1;boolean parsingName = true;boolean decodeName = false;boolean decodeValue = false;boolean parameterComplete = false;do {switch(bytes[pos]) {case '=':if (parsingName) {// Name finished. Value starts from next characternameEnd = pos;parsingName = false;valueStart = ++pos;} else {// Equals character in valuepos++;}break;case '&':if (parsingName) {// Name finished. No value.nameEnd = pos;} else {// Value finishedvalueEnd  = pos;}parameterComplete = true;pos++;break;case '%':case '+':// Decoding requiredif (parsingName) {decodeName = true;} else {decodeValue = true;}pos ++;break;default:pos ++;break;}} while (!parameterComplete && pos < end);if (pos == end) {if (nameEnd == -1) {nameEnd = pos;} else if (valueStart > -1 && valueEnd == -1){valueEnd = pos;}}if (log.isDebugEnabled() && valueStart == -1) {log.debug(sm.getString("parameters.noequal",Integer.valueOf(nameStart), Integer.valueOf(nameEnd),new String(bytes, nameStart, nameEnd-nameStart, DEFAULT_BODY_CHARSET)));}if (nameEnd <= nameStart ) {if (valueStart == -1) {// &&if (log.isDebugEnabled()) {log.debug(sm.getString("parameters.emptyChunk"));}// Do not flag as errorcontinue;}// &=foo&UserDataHelper.Mode logMode = userDataLog.getNextMode();if (logMode != null) {String extract;if (valueEnd > nameStart) {extract = new String(bytes, nameStart, valueEnd - nameStart,DEFAULT_BODY_CHARSET);} else {extract = "";}String message = sm.getString("parameters.invalidChunk",Integer.valueOf(nameStart),Integer.valueOf(valueEnd), extract);switch (logMode) {case INFO_THEN_DEBUG:message += sm.getString("parameters.fallToDebug");//$FALL-THROUGH$case INFO:log.info(message);break;case DEBUG:log.debug(message);}}setParseFailedReason(FailReason.NO_NAME);continue;// invalid chunk - it's better to ignore}tmpName.setBytes(bytes, nameStart, nameEnd - nameStart);if (valueStart >= 0) {tmpValue.setBytes(bytes, valueStart, valueEnd - valueStart);} else {tmpValue.setBytes(bytes, 0, 0);}// Take copies as if anything goes wrong originals will be// corrupted. This means original values can be logged.// For performance - only done for debugif (log.isDebugEnabled()) {try {origName.append(bytes, nameStart, nameEnd - nameStart);if (valueStart >= 0) {origValue.append(bytes, valueStart, valueEnd - valueStart);} else {origValue.append(bytes, 0, 0);}} catch (IOException ioe) {// Should never happen...log.error(sm.getString("parameters.copyFail"), ioe);}}try {String name;String value;if (decodeName) {urlDecode(tmpName);}tmpName.setCharset(charset);name = tmpName.toString();if (valueStart >= 0) {if (decodeValue) {urlDecode(tmpValue);}tmpValue.setCharset(charset);value = tmpValue.toString();} else {value = "";}try {addParameter(name, value);} catch (IllegalStateException ise) {// Hitting limit stops processing further params but does// not cause request to fail.UserDataHelper.Mode logMode = maxParamCountLog.getNextMode();if (logMode != null) {String message = ise.getMessage();switch (logMode) {case INFO_THEN_DEBUG:message += sm.getString("parameters.maxCountFail.fallToDebug");//$FALL-THROUGH$case INFO:log.info(message);break;case DEBUG:log.debug(message);}}break;}} catch (IOException e) {setParseFailedReason(FailReason.URL_DECODING);decodeFailCount++;if (decodeFailCount == 1 || log.isDebugEnabled()) {if (log.isDebugEnabled()) {log.debug(sm.getString("parameters.decodeFail.debug",origName.toString(), origValue.toString()), e);} else if (log.isInfoEnabled()) {UserDataHelper.Mode logMode = userDataLog.getNextMode();if (logMode != null) {String message = sm.getString("parameters.decodeFail.info",tmpName.toString(), tmpValue.toString());switch (logMode) {case INFO_THEN_DEBUG:message += sm.getString("parameters.fallToDebug");//$FALL-THROUGH$case INFO:log.info(message);break;case DEBUG:log.debug(message);}}}}}tmpName.recycle();tmpValue.recycle();// Only recycle copies if we used themif (log.isDebugEnabled()) {origName.recycle();origValue.recycle();}}if (decodeFailCount > 1 && !log.isDebugEnabled()) {UserDataHelper.Mode logMode = userDataLog.getNextMode();if (logMode != null) {String message = sm.getString("parameters.multipleDecodingFail",Integer.valueOf(decodeFailCount));switch (logMode) {case INFO_THEN_DEBUG:message += sm.getString("parameters.fallToDebug");//$FALL-THROUGH$case INFO:log.info(message);break;case DEBUG:log.debug(message);}}}}

其中url解码的方法

    private void urlDecode(ByteChunk bc)throws IOException {if( urlDec==null ) {urlDec=new UDecoder();}urlDec.convert(bc, true);}

解决方案:

我们把这个方法抽取为工具类,

可以看到这个方法中调用addParameter(name, value);就是将处理好的请求参数和值放到parameterMap中, 这里改成放到自定义的Map中, 在这个方法最后返回, 其他报错的地方看着改改。.

这样一个完美的请求参数解析工具类就搞出来啦。.

Java-Tomcat的请求参数解析分析相关推荐

  1. java控制器接收请求参数_SpringMVC之接收请求参数和页面传参

    Spring接收请求参数 1,使用HttpServletRequest获取 Java代码 @RequestMapping("/login.do") public String lo ...

  2. java发送get请求参数_HttpClient发送带参数的Get请求

    HttpClient 是apache 组织下面的一个用于处理HTTP 请求和响应的开源工具.所用jar包为httpclient-4.3.6.jar.httpcore-4.3.3.jar.httpmim ...

  3. Java发送Post请求,参数JSON,接收JSON

    /*** 发送post请求* @param url 路径* @param jsonObject 参数(json类型)* @param encoding 编码格式* @return* @throws P ...

  4. Java发送Http请求,解析html返回

    今天是2008年7月7日星期一,下午一直在学校做个人开始页面.因为离不开google的翻译,所以想把google的翻译整合到我的开始页面中来,于是乎就遇到了一个问题,怎样使用java程序发送http请 ...

  5. java forward 修改请求参数_聊聊springboot session timeout参数设置

    序 本文主要介绍下spring boot中对session timeout参数值的设置过程. ServerProperties spring-boot-autoconfigure-1.5.8.RELE ...

  6. python 参数解析_python的函数对参数解析分析

    以下转自其它博客.觉得总结得太好了,所以拿来自己参考一下. python中函数参数的传递是通过赋值来传递的. 函数参数的使用又有俩个方面值得注意: 1.函数参数是如何定义的 2.在调用函数的过程中参数 ...

  7. PYTHON的函数对参数解析分析

    以下转自其它博客.觉得总结得太好了,所以拿来自己参考一下. python中函数参数的传递是通过赋值来传递的. 函数参数的使用又有俩个方面值得注意: 1.函数参数是如何定义的 2.在调用函数的过程中参数 ...

  8. jmeter 使用BeanShell编写java代码提取请求参数

    之前在用jmeter写接口测试的时候,我想尽量把参数都设置成可变修改的,现在的大多数请求都会带上时间戳,于是,我用了两种方式提取可变的时间戳参数 1.直接在jmeter的GUI中,编写获取时间戳的方法 ...

  9. java发送get请求参数_get方式请求接受参数的方法

    1.获取jsp页面的url,然后通过js获取参数 function getQueryString(name) { var reg = new RegExp("(^|&)"+ ...

最新文章

  1. 安装linux系统选择重新分区会怎么样,弱弱的问一下 双系统安装中linux如何分区?分区太多会不会不好?...
  2. 【Android】 Android中Log调试详解
  3. sql int 比较_分享 21 个编写 SQL 的好习惯
  4. android工具栏设为底层,Android 隐藏底部工具栏
  5. 常用的织梦(dedecms)调用标签
  6. cartographer 前端两个方法:相干性匹配与非线性优化;以及回环检测方法:利用了分枝定界的相干性匹配
  7. linux内核之进程地址空间
  8. atitit.无线网卡 不能搜索到WiFi 无线路由信号的解决不能上网
  9. Android使用JSONObject解析接口json字符串(带日期)
  10. 【Matplotlib:隐藏边框,显示单个网格线,设置刻度间隔,隐藏刻度线】
  11. img图片在父元素中居中的方法
  12. Oracle数据库启动过程详解
  13. linux修改分区自检,linux tune2fs命令取消大分区开机自检
  14. 知晓云深坑:获取不到数据
  15. 论文浅尝 | PAKDD2020 - 利用支持集中匹配信息的 few shot 事件分类方法
  16. Android通过广播接收器BroadcastReceiver监听蓝牙连接变化
  17. 计算机系统基础——我与袁春风不得不说的知识——入门必看
  18. 小班计算机游戏教案,小班游戏教案10篇
  19. 实时系统vxWorks - timer定时应用
  20. 将SoundCloud API与Jav​​aScript SDK结合使用

热门文章

  1. 数据可视化——彩色通用设计之色彩搭配(制作对色盲人群友好的图形和演示)
  2. pigx框架费用_【开源项目】一篇文章搞掂:Pig微服务框架
  3. 代码 马佳义_212电子信息学院(2018年度)
  4. python永久删除文件_Python彻底删除文件夹及其子文件方式
  5. RecycleView实现像ViewPager一次滑一页效果
  6. 大物下(大学物理知识点回顾与典型题解析
  7. 使用插件实现ecplise js/jquery智能提示
  8. 非华为电脑多屏协同安装最新的电脑管家
  9. 微信小程序image图片预览时不显示(后缀问题!)
  10. RK3399 Android 7.1开发准备