HttpClient POST 的 UTF-8 编码问题

本文介绍如何解决Apache HttpClient在使用UTF-8编码时出现的乱码问题,并提供了一个具体的例子来展示如何通过覆盖`getRequestCharSet()`方法来实现。
 Apache HttpClient ( http://jakarta.apache.org/commons/httpclient/ ) 是一个纯 Java 的HTTP 协议的客户端编程 工具包, 对 HTTP 协议的支持相当全面, 更多细节也可以参考IBM 网站上的这篇文章 HttpClient入门 ( http://www-128.ibm.com/developerworks/cn/opensource/os-httpclient/ ).


问题分析不过在实际使用中, 还是发现按照最基本的方式调用 HttpClient 时, 并不支持 UTF-8 编码, 在 网络上找过一些文章, 也不得要领, 于是查看了 commons-httpclient-3.0.1 的一些代码, 首先在 PostMethod 中找到了 generateRequestEntity() 方法:
    /**
     * Generates a request entity from the post parameters, if present.  Calls
     * {@link EntityEnclosingMethod#generateRequestBody()} if parameters have not been set.
     *
     * @since 3.0
     */
    protected RequestEntity generateRequestEntity() {
        if (!this.params.isEmpty()) {
            // Use a ByteArrayRequestEntity instead of a StringRequestEntity.
            // This is to avoid potential encoding issues.  Form url encoded strings
            // are ASCII by definition but the content type may not be.  Treating the content
            // as bytes allows us to keep the current charset without worrying about how
            // this charset will effect the encoding of the form url encoded string.
            String content = EncodingUtil.formUrlEncode(getParameters(), getRequestCharSet());
            ByteArrayRequestEntity entity = new ByteArrayRequestEntity(
                EncodingUtil.getAsciiBytes(content),
                FORM_URL_ENCODED_CONTENT_TYPE
            );
            return entity;
        } else {
            return super.generateRequestEntity();
        }
    }

原来使用 NameValuePair 加入的 HTTP 请求的参数最终都会转化为 RequestEntity 提交到 HTTP 服务器, 接着在 PostMethod 的父类 EntityEnclosingMethod 中找到了如下的代码:
    /**
     * Returns the request's charset.  The charset is parsed from the request entity's
     * content type, unless the content type header has been set manually.
     *
     * @see RequestEntity#getContentType()
     *
     * @since 3.0
     */
    public String getRequestCharSet() {
        if (getRequestHeader("Content-Type") == null) {
            // check the content type from request entity
            // We can't call getRequestEntity() since it will probably call
            // this method.
            if (this.requestEntity != null) {
                return getContentCharSet(
                    new Header("Content-Type", requestEntity.getContentType()));
            } else {
                return super.getRequestCharSet();
            }
        } else {
            return super.getRequestCharSet();
        }
    }


解决 方案从上面两段代码可以看出是 HttpClient 是如何依据 "Content-Type" 获得请求的编码(字符集), 而这个编码又是如何应用到提交内容的编码过程中去的. 按照这个原来, 其实我们只需要重载 getRequestCharSet() 方法, 返回我们需要的编码(字符集)名称, 就可以 解决 UTF-8 或者其它非默认编码提交 POST 请求时的乱码问题了.

测试首先在 Tomcat 的 ROOT WebApp 下部署一个 页面 test.jsp, 作为测试 页面, 主要代码片段如下:
<%@ page contentType="text/html;charset=UTF-8"%>
<%@ page session="false" %>
<%
request.setCharacterEncoding("UTF-8");
String val = request.getParameter("TEXT");
System.out.println(">>>> The result is " + val);
%>


接着写一个测试类, 主要代码如下:
    public static void main(String[] args) throws Exception, IOException {
        String url = "http://localhost:8080/test.jsp";
        PostMethod postMethod = new UTF8PostMethod(url);
        //填入各个表单域的值
        NameValuePair[] data = {
                new NameValuePair("TEXT", "中文"),
        };
        //将表单的值放入postMethod中
        postMethod.setRequestBody(data);
        //执行postMethod
        HttpClient httpClient = new HttpClient();
        httpClient.executeMethod(postMethod);
    }
   
    //Inner class for UTF-8 support
    public static class UTF8PostMethod extends PostMethod{
        public UTF8PostMethod(String url){
            super(url);
        }
        @Override
        public String getRequestCharSet() {
            //return super.getRequestCharSet();
            return "UTF-8";
        }
    }


运行这个测试程序, 在 Tomcat 的后台输出中可以正确打印出 ">>>> The result is 中文" .
### 使用UTF-8编码的意义 UTF-8是一种可变长度的字符编码标准,能够兼容ASCII并支持全球范围内的几乎所有字符[^1]。它通过使用一到四个字节来表示单个字符的方式,在保持向后兼容的同时扩展了对多种语言的支持能力。这种特性使得UTF-8成为Web开发中最广泛使用的字符编码之一。 在HTTP协议下的POST请求中,数据通常作为实体主体被发送至服务器端。由于这些数据可能包含来自不同语言环境的信息(如中文、日文或其他非拉丁语系文字),如果缺乏统一的编码约定,则可能导致接收方无法正确解析所传递的内容,进而引发诸如乱码等问题。因此,指定使用UTF-8编码可以有效减少因字符集不匹配而产生的错误情况发生几率[^2]。 ### 实现方式 为了确保POST请求的数据能以UTF-8形式传输给服务端,并让后者按照相同的标准去解读收到的消息体内容,可以从以下几个方面着手: #### 设置客户端请求头字段 当构建一个基于Java语言实现的HttpURLConnection实例用于发起POST操作时,可以通过设置特定属性告知目标资源期望接收到什么样的格式化输入流。例如下面这段代码展示了如何配置必要的头部信息以便于后续处理阶段识别出正确的编码类型: ```java // 创建URL对象并打开连接 URL url = new URL("http://example.com/api"); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); // 配置连接选项 conn.setDoOutput(true); conn.setDoInput(true); // 定义请求方法为POST conn.setRequestMethod("POST"); // 设定相关Header参数 byte[] requestStringBytes = param.getBytes("UTF-8"); conn.setRequestProperty("Content-Length", Integer.toString(requestStringBytes.length)); conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8"); conn.setRequestProperty("Connection", "Keep-Alive"); conn.setRequestProperty("Charset", "UTF-8"); ``` 以上片段重点在于`Content-Type`声明部分指定了媒体类型的子类型及其附加参数——即这里的charset=UTF-8表明整个消息体均遵循该规则进行序列化过程[^5]。 #### 应用框架层面全局设定 除了手动调整每次单独发出网络通信前后的细节外,还可以借助某些高级别的抽象工具或者中间件机制达成一致性的效果。比如Tomcat容器内置了一个过滤器组件叫做`Set Character Encoding Filter`, 只需简单修改web.xml文件即可完成跨站点范围内标准化管理需求: ```xml <filter> <filter-name>encodingFilter</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>encodingFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> ``` 此方案不仅简化了开发者的工作量而且增强了系统的健壮性和用户体验满意度水平[^3]. 另外值得注意的是, 对于一些第三方库而言也可能存在类似的便捷途径可供选用. 例如Apache HttpClient提供了自定义继承类覆盖默认行为的功能点, 如下所示例子演示了怎样重写父类方法返回固定值从而强制应用层面上始终采纳预设好的编码策略. ```java public static class CustomPostMethod extends PostMethod { public CustomPostMethod(String uri){ super(uri); } @Override public String getRequestBodyCharset() throws IOException{ return "UTF-8"; } } ``` 综上所述, 正确运用好UTF-8编码不仅可以提高程序运行效率还能增强国际化适配程度, 是现代软件工程实践中不可或缺的重要组成部分.[^4]
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值