URIEncoding与useBodyEncodingForURI 在tomcat中文乱码处理上的区别及CharacterEncodingFilter(备忘)

时间:2023-01-10 18:01:22

大家知道tomcat5.0开始,对网页的中文字符的post或者get,经常会出现乱码现象。

具体是因为Tomcat默认是按ISO-8859-1进行URL解码,ISO-8859-1并未包括中文字符,这样的话中文字符肯定就不能被正确解析了。

常见的解决方法是在tomcat的server.xml下的connetor属性中增加URIEncoding或者useBodyEncodingForURI属性。

但是,这两种方式有什么区别呢?

我简单谈一下自己的理解:

按照tomcat-docs/config/http.html文档的说明

URIEncoding:This specifies the character encoding used to decode the URI bytes, after %xx decoding the URL. If not specified, ISO-8859-1 will be used. 

useBodyEncodingForURI:This specifies if the encoding specified in contentType should be used for URI query parameters, instead of using the URIEncoding.

 

也就是说,

 useBodyEncodingForURI参数表示是否用request.setCharacterEncoding 
参数对URL提交的数据和表单中GET方式提交的数据进行重新编码,在默认情况下,该参数为false。

URIEncoding参数指定对所有GET方式请求进行统一的重新编码(解码)的编码。

 

URIEncoding和useBodyEncodingForURI区别是,

URIEncoding是对所有GET方式的请求的数据进行统一的重新编码,

而useBodyEncodingForURI则是根据响应该请求的页面的request.setCharacterEncoding参数对数据进行的重新编码,不同的页面可以有不同的重新编码的编码



再详细点的======================================================================================================

本文章会从tomcat的源码角度来解析Tomcat的两个参数设置URIEncoding和useBodyEncodingForURI。 

对于一个请求,常用的有两种编码方式,如下:
 

Java代码  URIEncoding与useBodyEncodingForURI 在tomcat中文乱码处理上的区别及CharacterEncodingFilter(备忘)
  1. <!DOCTYPE html>  
  2. <html>  
  3.     <head>  
  4.         <meta charset="utf-8" />  
  5.         <title></title>  
  6.     </head>  
  7.     <body>  
  8.         <form action="http://127.0.0.1:8080/string?name=中国" method="post">  
  9.             <input type="text" name="user" value="张三"/>  
  10.             <input type="submit" value="提交"/>  
  11.         </form>  
  12.     </body>  
  13. </html>  

首先说说结论: 
上述请求有两处含有中文,一处是请求参数中,即?name='中国',另一处是请求体中,即user='张三'。对于这两处tomcat7是分两种编码方式的。URIEncoding就是针对请求参数的编码设置的,而filter的request.setCharacterEncoding('UTF-8')或者请求header中的content-type中的编码都是针对请求体的。不要把他们搞混了。 

useBodyEncodingForURI=true是说,请求参数的编码方式要采用请求体的编码方式。当useBodyEncodingForURI=true时,若请求体采用utf-8解析,则请求参数也要采用utf-8来解析。这两个属性值的设置在tomcat的conf/server.xml文件中配置,如下:
 
Java代码  URIEncoding与useBodyEncodingForURI 在tomcat中文乱码处理上的区别及CharacterEncodingFilter(备忘)
  1. <Service name="Catalina">  
  2.   
  3.    <!--The connectors can use a shared executor, you can define one or more named thread pools-->  
  4.    <!--  
  5.    <Executor name="tomcatThreadPool" namePrefix="catalina-exec-"  
  6.        maxThreads="150" minSpareThreads="4"/>  
  7.    -->  
  8.   
  9.   
  10.    <!-- A "Connector" represents an endpoint by which requests are received  
  11.         and responses are returned. Documentation at :  
  12.         Java HTTP Connector: /docs/config/http.html (blocking & non-blocking)  
  13.         Java AJP  Connector: /docs/config/ajp.html  
  14.         APR (HTTP/AJP) Connector: /docs/apr.html  
  15.         Define a non-SSL HTTP/1.1 Connector on port 8080  
  16.    -->  
  17.    <Connector port="8080" protocol="HTTP/1.1"  
  18.               connectionTimeout="20000"  
  19.               redirectPort="8443" useBodyEncodingForURI='true' URIEncoding='UTF-8' />  
  20.    <!-- A "Connector" using the shared thread pool-->  

这样写只是说明这两者的配置位置,并不是两个属性要同时配置,不要理解错了。 
继续说说CharacterEncodingFilter的作用。 
使用方式,将如下代码加入web.xml文件中:
 
Java代码  URIEncoding与useBodyEncodingForURI 在tomcat中文乱码处理上的区别及CharacterEncodingFilter(备忘)
  1. <filter>  
  2.         <filter-name>encoding</filter-name>  
  3.         <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>  
  4.         <init-param>  
  5.             <param-name>encoding</param-name>  
  6.             <param-value>UTF-8</param-value>  
  7.         </init-param>  
  8.         <init-param>  
  9.             <param-name>forceEncoding</param-name>  
  10.             <param-value>true</param-value>  
  11.         </init-param>  
  12.     </filter>  
  13.   
  14.     <filter-mapping>  
  15.         <filter-name>encoding</filter-name>  
  16.         <url-pattern>/*</url-pattern>  
  17.     </filter-mapping>  

作用是,当forceEncoding为false的前提下(默认为false),当request没有指定content-type或content-type不含编码时,该filter将会为这个request设定请求体的编码为filter的encoding值。 
当forceEncoding为true的前提下,就会为request的请求体和response都设定为这个filter的encoding值。 
CharacterEncodingFilter源码如下:
 
Java代码  URIEncoding与useBodyEncodingForURI 在tomcat中文乱码处理上的区别及CharacterEncodingFilter(备忘)
  1. public class CharacterEncodingFilter extends OncePerRequestFilter {  
  2.   
  3.     private String encoding;  
  4.   
  5.     private boolean forceEncoding = false;  
  6.   
  7.   
  8.     /** 
  9.      * Set the encoding to use for requests. This encoding will be passed into a 
  10.      * {@link javax.servlet.http.HttpServletRequest#setCharacterEncoding} call. 
  11.      * <p>Whether this encoding will override existing request encodings 
  12.      * (and whether it will be applied as default response encoding as well) 
  13.      * depends on the {@link #setForceEncoding "forceEncoding"} flag. 
  14.      */  
  15.     public void setEncoding(String encoding) {  
  16.         this.encoding = encoding;  
  17.     }  
  18.   
  19.     /** 
  20.      * Set whether the configured {@link #setEncoding encoding} of this filter 
  21.      * is supposed to override existing request and response encodings. 
  22.      * <p>Default is "false", i.e. do not modify the encoding if 
  23.      * {@link javax.servlet.http.HttpServletRequest#getCharacterEncoding()} 
  24.      * returns a non-null value. Switch this to "true" to enforce the specified 
  25.      * encoding in any case, applying it as default response encoding as well. 
  26.      * <p>Note that the response encoding will only be set on Servlet 2.4+ 
  27.      * containers, since Servlet 2.3 did not provide a facility for setting 
  28.      * a default response encoding. 
  29.      */  
  30.     public void setForceEncoding(boolean forceEncoding) {  
  31.         this.forceEncoding = forceEncoding;  
  32.     }  
  33.   
  34.   
  35.     @Override  
  36.     protected void doFilterInternal(  
  37.             HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)  
  38.             throws ServletException, IOException {  
  39.   
  40.         if (this.encoding != null && (this.forceEncoding || request.getCharacterEncoding() == null)) {  
  41.             request.setCharacterEncoding(this.encoding);  
  42.             if (this.forceEncoding) {  
  43.                 response.setCharacterEncoding(this.encoding);  
  44.             }  
  45.         }  
  46.         filterChain.doFilter(request, response);  
  47.     }  
  48.   
  49. }  

这个filter有两个属性,encoding和forceEncoding,我们可以在web.xml文件中来设定这两个属性值。 
每次request请求到来执行方法doFilterInternal,首先调用request.getCharacterEncoding(),本质就是从请求header content-type中获取编码值,如果没有,则调用request.setCharacterEncoding(this.encoding)将该filter的encoding值设置为请求体的编码方式,记住该编码方式只对请求体,不针对请求参数。当forceEncoding=true时,不管请求header content-type有没有编码方式,始终将该filter的encoding值设置到request和response中,同样只针对request的请求体。