总结:
HTTP GET方式
无forward操作,解码只受Connector参数影响(URIEncoding或者UseBodyEncondig)
有forward操作,解码只受HTTP Header中ContentType指定的字符集或者Filter设置影响
多次forward的话,forward后,第一次从请求取值后,tomcat进行了参数解码,后面就不会再次解码(即多次forward的情况,只有2次解码动作,forward之前和forward之后第一次从request取值)
HTTP GET方式
不管有无forward操作,解码只受HTTP Header中ContentType指定的字符集或者Filter设置影响(第一次从request取值时对应应用环境下的Filter)
request.getParameterValues和request.getParameter的逻辑是类似的,都是看相应的Request参数是否已经转换过
(调用getParameter, getParameterValues, getParameterMap, getParameterNames这4个方法都会触发参数转换操作),没有那么调用相应的转换程序,否则就直接操作转换好的paramters(HashMap)
ApplicationDispatcher是RequestDispatcher接口的final标准实现,允许请求forward到其他资源进行服务(include也使用该类)。这个实现是在Application应用程序级别的Servlet中对请求和响应进行包装(ServletRequest,ServletResponse或者ServletRequestWrapper,ServletResponseWrapper)
参考:
final class: ApplicationDispatcher
RequestDispatcher.forward前使用的是,RequestFacade;forward之后使用的是ApplicationHttpRequest。它们对用来给请求解码的编码的默认值处理不同,具体参考如下。而
如果在forward之前,调用过request.getParameter方法来取值,那么forward后,CoyteRequest内的信息可能被解码,但是当前请求ApplicationHttpRequest对应的参数还没有解码!2部分参数会合成并且当前请求ApplicationHttpRequest对应的参数是放在前面的!
注意:此时重新解码的参数是Get方式的QueryString的参数,POST提交的参数还是在RequestFacade进行解码!!!
forward之前从RequestFacade取值
1,调用request.getParameterValues方法
2,对应org.apache.catalina.connector.
RequestFacade类的getParameterValues方法(RequestFacade是一个HttpServletRequest实现类,他包含一个叫Coyote request的属性,具体请求执行内容由具体的Coyote request决定)
3,调用org.apache.catalina.connector.Request的getParameterValues方法,
如果第一次取调用该方法(即还没有做过参数转换----把QueryString或者POST上来的比特数组通过设定的编码转成真正的值),
那么进行参数转换操作,否则直接从已经转换好的参数列表中提取相应参数的值
4,参数转换:
- a.取得Request字符集enc:1,看请求本身是否已经被设置了字符集(通过request.setCharacterEncoding设置),如果有直接返回,如果没有,那么根据请求的Content-Type,看Content-Type中是否通过charset=xxx设置了字符集,有就起作用,没有就返回null
- b.取得Connect参数UseBodyEncodingForURI对应的值
- c.如果a)步骤取得的Request字符集enc非空,那么Parameters字符集等于Request字符集,如果UseBodyEncodingForURI为true,那么QueryString解码使用的字符集QueryStringEncoding的值也设置为请求字符集(和Parameters一致)
- d.如果a)步骤取得的Request字符集enc为空,那么Parameters字符集等于默认的字符集ISO-8859-1,如果UseBodyEncodingForURI为true,那么QueryString解码使用的字符集QueryStringEncoding的值也设置为默认的字符集ISO-8859-1(和Parameters一致)
- e.对QueryString所传递的参数进行解码操作(根据QueryStringEncoding)
- f.看HTTP METHOD是否等于POST,同时看Content-Type是否以application/x-www-form-urlencoded开始,是的话,对通过POST方式上传的比特数组byte[] postData,用d)步骤取得的Parameters字符集进行解码(如果没有值,就默认用ISO-8859-1),并把解码后的参数值保存到参数列表中等待下次复用(只解码一次!!!)
forward之后从ApplicationHttpRequest取值
注意,forward之后,这个Request中解码的只有QueryString的参数,且从request取值的时候刚解码的参数优先级高(数组下标),而post提交的参数,会直接调用ApplicationHttpRequest中嵌套的RequestFacade的相应方法取值,即Post方式提交的参数的解码,受到forward之前请求的相关环境的影响!!!
1,调用request.getParameterValues方法
2,对应8, org.apache.catalina.core.
ApplicationHttpRequest类的getParameterValues方法(ApplicationHttpRequestS有一个ServletRequest的属性,而RequestFacade是ServletRequest的一个属性),
由此可以看出,对于参数取值,forward后的Request多一层包装。
3,调用org.apache.catalina.core.ApplicationHttpRequest的getParameterValues方法,
如果参数还没有被转换过那么进行参数转换操作,否则直接从已经转换好的参数列表中提取相应参数的值
4,参数转换:
- a.取得已有的参数(即从当前request中取得CoyteRequest中已有的参数,注意:如果一个url前面做过forward的话,并且在forward之前已经从Request取过值的话,CoyteRequest中的取得的参数是已经转换过的!!!但是ApplicationHttpRequest本身的Parameter并没有被转换过!!!)代码如下:parameters = new HashMap();parameters = copyMap(getRequest().getParameterMap());
- b.调用mergeParameters方法,把ApplicationHttpRequest自己的参数QueryString对应的参数值解码出来,并和CoyteRequest中已有的参数合并,刚解码出来的参数放到数组前面,这样你使用Request.getParametr来取字段的值的时候,取出来的是刚解码的值,而如果用取数组的方法,那么取出来是整个数组。
ApplicationHttpRequest解码自己参数的逻辑:
取得Request字符集,如果取不到,就直接使用ISO-8859-1字符集来解码。然后调用RequestUtil.parseParameters(queryParameters, queryParamString, encoding)来进行解码操作。
表明此时,Connector的参数UseBodyEncodingForURI和URIEncoding并没有影响QueryString的解码。
5,
总结:
- a.QueryStringEncoding:用来解码QueryString信息
- b.Request字符集:Content-Type中的字符集影响的是Request字符集,并不影响QueryStringEncoding,间接影响Parameters字符集
- c.Parameters字符集:用来解码POST参数【比特数组】时使用的字符集,如果Request字符集非空,那么Parametr是字符集=Request字符集,否则等于默认字符集ISO-8859-1。Parameters字符集用来解码POST DATA对应的比特数组(解码后在JVM中以Unicode的形式存在)
- d.参考Tomcat源码分析--HTTP,AJP请求内部处理流程,可以看出对于RequestFacade请求,QueryStringEncoding的值首先从Connector中的参数URIEncoding中取,同时参考下面的说明,可知useBodyEncodingForURI的优先级 大于URIEncoding
- e.Request字符集:如果没有通过request.setCharacterEncoding方法设置字符集,那么默认从Content-Type中提取charset=xxx的值,如果通过request.setCharacterEncoding方法设置了新的字符集,那么新的字符集会覆盖Content-Type中的字符集
- f.如果设置了Connector的参数useBodyEncodingForURI=true,那么把QueryStringEncoding值设置为和Request字符集相同!