如题,在Java中也许很多人都没有注意到当我们发送一个http请求给时,如果附带的参数被URLEncode之后,到达web容器之后,开发者获取到的参数值会自动变成了encode之前的值。这是一个很好的特点,开发者完全可以忽略http的参数是否需要decode这种事,但是decode到底是在什么发生的呢?
一时兴起就陷了进去,于是就开始一层层的找起了。
第一步就是从request.getParameter()方法下手,但是ServletRequest只是一个接口,是J2EE定义的Servlet框架的一个基本接口,具体实现还是得看具体的Servlet容器,即Web容器,我用的是JBOSS,所以就从JBOSS中源码着手。同时为了找出当我们调用request.getParameter()时具体是调用哪个ServletRequest对象的方法,使用了反射来查找,代码如下:
System.out.println(request.getClass().getName());
很简单,通过request的getClass获取对象的具体名称,得到的结果是:org.apache.catalina.connector.RequestFacade。可见是Web容器提供的具体ServletRequest实现类,既然找到了具体的类接下来肯定是去看下API文档,看看有没有提到会对request参数值进行decode操作,顺便说下org.apache.catalina.connector.RequestFacade时Tomcat的API,果然文档并未提到任何关于decode的内容,反倒看到RequestFacade仅仅是一个包装器,真正工作的竟然另有其人,好吧,只能找到代码了。
找到RequestFacade的源码,发现真正用于获取request参数的类是RequestFacade的一个受保护的变量request,当然这个类也是实现了ServletRequest接口的。继续查看Request源码,发现真正干活的类还不是它,Request内部还有一个变量org.apache.coyote.Request.coyoteRequest,继续找到org.apache.coyote.Request源码。找到org.apache.coyote.Request源码大致就已经可以看出到底是什么时候进行的decode操作了。在org.apache.coyote.Request的构造器中可以看见:
public Request() { this.parameters.setQuery(this.queryMB); this.parameters.setURLDecoder(this.urlDecoder); this.parameters.setHeaders(this.headers); this.methodMB.setString("GET"); this.uriMB.setString("/"); this.queryMB.setString(""); this.protoMB.setString("HTTP/1.0"); }
其中一句this.parameters.setURLDecoder(this.urlDecoder);指定了URLDecoder工具类,再来看看parameters可以看到该类就是用来解析http请求参数的类,该类会使用URLDecoder工具类对请求的name以及value进行decode操作,到这里基本就已经看到了HTTP请求中的参数到底是如何被自动decode的了。其中并没有去深究代码步骤,毕竟我们只要知道是什么进行的decode即可。
从上面的整个流程可以看出,对HTTP参数进行自动decode是Web容器依赖的,即并非J2EE标准,所以其他Web容器有可能并未做这种操作,所以开发者还是得注意。