ITEEDU

编程学习网


  • 首页

  • 标签

  • 分类

  • 归档

  • 关于

  • 旧站归档

  • 搜索

反向代理之http请求乱码分析

发表于 2017-03-08 更新于 2019-06-29 分类于 架构 , 反向代理

请求测试代码

import java.util.HashMap;
import java.util.Map;

import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.NameValuePair;
import org.apache.commons.httpclient.methods.PostMethod;

public class HttpCharsetTest {
    public static void main(String[] args) throws Exception {
        String strUrl = "http://localhost:8080/front_new/rproxy/123/456";
        Map<String, String> requestParams = new HashMap<String, String>();
        requestParams.put("testkey", "测试");
        int i = 0;
        NameValuePair[] nvps = new NameValuePair[requestParams.size()];
        for (String strKey : requestParams.keySet()) {
            NameValuePair nvp = new NameValuePair();
            nvp.setName(strKey);
            nvp.setValue(requestParams.get(strKey));
            nvps[i] = nvp;
            i++;
        }
        PostMethod postMethod = null;
        HttpClient httpClient = new HttpClient();
        // 设置超时时间
        httpClient.getHttpConnectionManager().getParams()
                .setConnectionTimeout(30000);
        httpClient.getHttpConnectionManager().getParams().setSoTimeout(30000);
        postMethod = new PostMethod(strUrl);
        postMethod.setRequestBody(nvps);
//        postMethod.addRequestHeader("Content-Type", "text/html;charset=UTF-8");
//        httpClient.getParams().setContentCharset("UTF-8");
//        postMethod.getParams().setContentCharset("UTF-8");
        httpClient.executeMethod(postMethod);
        String resultStr = postMethod.getResponseBodyAsString();
        System.err.println(resultStr);
    }
}

httpClient请求编码设置

postMethod.addRequestHeader("Content-Type", "text/html;charset=UTF-8");
httpClient.getParams().setContentCharset("UTF-8");
postMethod.getParams().setContentCharset("UTF-8");

以上三种方式,下面的设置方法会覆盖上面的设置。

内容查看代码

获取请求完整内容:

byte[] data=IOUtils.readBytesFromStream(request.getInputStream());
String strData=new String(data);
String strDecode=URLDecoder.decode(strData, "UTF-8");

内容测试

不同的httpClient请求编码,参数内容“测试”二字,方式post,strData内容如下:

不设置字符集
testkey=%3F%3F
UTF-8字符集
testkey=%E6%B5%8B%E8%AF%95
GBK字符集
testkey=%B2%E2%CA%D4

如果不设置字符集,每个汉字编码成占一个字节的编码,没找到任何一个编码与之对应URLDecoder.decode后是乱码。

UTF-8编码成三个字节一个汉字,GBK编码成二个字节一个汉字,用对应编码URLDecoder.decode后都是正常汉字。

获取请求参数

在获取参数调用request.getParameter前,要设置字符编码:

request.setCharacterEncoding("UTF-8");

可以通过设置spring的filter统一设置编码:

<filter>
  <filter-name>characterEncodingFilter</filter-name>
  <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
  <init-param>
    <param-name>encoding</param-name>
    <param-value>UTF-8</param-value>
  </init-param>
  <init-param>
    <param-name>forceEncoding</param-name>
    <param-value>true</param-value>
  </init-param>
</filter>
<filter-mapping>
  <filter-name>characterEncodingFilter</filter-name>
  <url-pattern>/*</url-pattern>
</filter-mapping>

spring的filter的实质:

@Override
protected void doFilterInternal(
        HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
        throws ServletException, IOException {

    if (this.encoding != null && (this.forceEncoding || request.getCharacterEncoding() == null)) {
        request.setCharacterEncoding(this.encoding);
        if (this.forceEncoding) {
            response.setCharacterEncoding(this.encoding);
        }
    }
    filterChain.doFilter(request, response);
}

encoding的作用相当于:

request.setCharacterEncoding

forceEncoding为true的作用相当于:

request.setCharacterEncoding(“”);
response.setCharacterEncoding(“”);

乱码详解

一个Http请求的数据大致包括URI、Header、和Body三个部分。这三个部分都需要encoding,不过一般只涉及到URI和Body,因此就不讨论Header了。

GET的请求参数在QueryString中,是URI的一部分。因此,对于GET请求,我们需要关注,URI是如何encoding的?

POST的请求参数在Body中,因此,对于POST请求,我们则需要关注,Body是如何encoding的?

麻烦的是,URI和Body的charset还可以不一样,使用不同的方法进行设置和配置。

Tomcat如何设置charset

Tomcat通过URI的charset来设置QueryString的charset。我们可以在Tomcat根目录下conf/server.xml 中进行配置。

<Connector
URIEncoding="UTF-8"
useBodyEncodingForURI="true" 
acceptCount="100"
connectionTimeout="20000"
disableUploadTimeout="true"
enableLookups="false"
maxHttpHeaderSize="8192"
maxSpareThreads="75"
maxThreads="150"
minSpareThreads="25"
port="8080"
redirectPort="8443"/>

URIEncoding属性就是URI的charset,上述配置表示 Tomcat认为URI的charset就是UTF-8。如果HttpClient也使用UTF-8作为QueryString的charset,那么Tomcat就可以正确decoding。详情可以参考org.apache.tomcat.util.http.Parameters类的handleQueryParameters的方法。

Tomcat在启动的过程中,如果从conf/server.xml中读取到URIEncoding属性,就会设置queryStringEncoding的值。当Tomcat处理HTTP请求时,上述方法就会被调用。

默认的server.xml是没有配置URIEncoding属性的,需要我们手动设置 。如果没有设置,Tomcat就会采用一种称为“fast conversion”的方式解析QueryString。详情可以参考org.apache.tomcat.util.http.Parameter类的urlDecode方法。

useBodyEncodingForURI是与URI charset相关的另一个属性。如果该属性的值为true,则Tomcat将使用Body的charset作为URI的charset。如果Tomcat没有设置Body的charset,那么将使用HTTP请求Content-Type Header中的charset。如果HTTP请求中没有设置Content-Type Header,则使用ISO-8859-1作为默认charset。详情参见org.apache.catalina.connector.Request的parseParmeters方法

默认的server.xml是没有配置useBodyEncodingForURI属性的,需要我们手动设置 。如果没有设置,Tomcat则认为其值为false。需要注意的是,如果URIEncoding和useBodyEncodingForURI同时设置,而且Body的charset已经设置,那么将以Body的charset为准 。

设置POST请求Body的charset:

设置Body charset的方法很简单,只要调用javax.servlet.ServletRequest接口的setCharacterEncoding方法即可,比如request.setCharacterEncoding(“UTF-8”)。需要注意的是,该方法必须在读取任何请求参数之前调用,才有效果。

也就是说,我们只有在调用getParameter或getReader方法之前,调用setsetCharacterEncoding方法,设置的charset才能奏效。

响应数据的charset:

设置响应数据charset的方法很简单,只要调用javax.servlet.ServletResponse接口的setContentType或setCharacterEncoding方法即可,比如response.setContentType(“text/html;charset=UTF-8”)或response.setCharacterEncoding(“UTF-8”)。

如果Servlet正确设置了响应数据的charset,那么HTTP响应数据中就会包含Content-Type Header。HttpClient的getResponseBodyAsString方法就可以正确decoding响应数据。

证心 微信支付

微信支付

# HttpClient
反向代理之http请求分析
反向代理之http响应乱码分析
  • 文章目录
  • 站点概览

证心

367 日志
45 分类
78 标签
GitHub 微博
  1. 1. 请求测试代码
  2. 2. 内容查看代码
  3. 3. 内容测试
  4. 4. 获取请求参数
  5. 5. 乱码详解
  6. 6. Tomcat如何设置charset
京ICP备16069454号 © 2020 证心