一次 http 请求涉及的编解码
URL 的编解码
URL 的组成部分
以 Tomcat 服务器为例子, 其中 Port,ContextPath 在 Server.xml 中配置
- <Connector connectionTimeout="20000" port="8080" protocol="HTTP/1.1" redirectPort="8443"
- />
- <Context docBase="examples" path="/examples" reloadable="true" source="org.eclipse.jst.jee.server:examples"
- />
ServletPath 在 web 应用的 Web.xml 中的 < url-pattern > 中配置, PathInfo 是我们请求的具体的 Servlet,QueryString 是要传递的参数, 注意这里是在浏览器里直接输入 URL 所以是通过 Get 方法请求的, 如果是 POST 方法请求的话, QueryString 将通过表单方式提交到服务器端.
通过浏览器的请求过程, 我们可知两个 "君山" 的编码结果是不同的: e5 90 9b e5 b1 b1,be fd c9 bd. 说明了 PathInfo 和 QueryString 是由不同的逻辑代码处理的.
PathInfo 的处理取决于 Server.xml 中的 Connector 标签中的定义, 如果没有定义, 那么将以默认编码 ISO-8859-1 解析. 所以如果有中文 URL 时最好把 URIEncoding 设置成 UTF-8 编码.
QueryString 又如何解析? GET 方式 HTTP 请求的 QueryString 与 POST 方式 HTTP 请求的表单参数都是作为 Parameters 保存, 都是通过 request.getParameter 获取参数值. 对它们的解码是在 request.getParameter 方法第一次被调用时进行的. request.getParameter 方法被调用时将会调用 org.apache.catalina.connector.Request 的 parseParameters 方法. 这个方法将会对 GET 和 POST 方式传递的参数进行解码, 但是它们的解码字符集有可能不一样. POST 表单的解码将在后面介绍, QueryString 的解码字符集要么是 Header 中 ContentType 中定义的 Charset 要么就是默认的 ISO-8859-1, 要使用 ContentType 中定义的编码就要设置 connector 的 ? 中的 useBodyEncodingForURI 设置为 true. 这个配置项的名字有点让人产生混淆, 它并不是对整个 URI 都采用 BodyEncoding 进行解码而仅仅是对 QueryString 使用 BodyEncoding 解码, 这一点还要特别注意.
所以在我们的应用程序中应该尽量避免在 URL 中使用非 ASCII 字符, 不然很可能会碰到乱码问题, 当然在我们的服务器端最好设置 ? 中的 URIEncoding 和 useBodyEncodingForURI 两个参数. 如下图:
HTTP Header 的编解码
当客户端发起一个 HTTP 请求除了上面的 URL 外还可能会在 Header 中传递其它参数如 Cookie,redirectPath 等, 这些用户设置的值很可能也会存在编码问题, Tomcat 对它们又是怎么解码的呢?
对 Header 中的项进行解码也是在调用 request.getHeader 是进行的, 如果请求的 Header 项没有解码则调用 MessageBytes 的 toString 方法, 这个方法将从 byte 到 char 的转化使用的默认编码也是 ISO-8859-1, 而我们也不能设置 Header 的其它解码格式, 所以如果你设置 Header 中有非 ASCII 字符解码肯定会有乱码.
我们在添加 Header 时也是同样的道理, 不要在 Header 中传递非 ASCII 字符, 如果一定要传递的话, 我们可以先将这些字符用 org.apache.catalina.util.URLEncoder 编码然后再添加到 Header 中, 这样在浏览器到服务器的传递过程中就不会丢失信息了, 如果我们要访问这些项时再按照相应的字符集解码就好了.
POST 表单中的编码
通过 POST 表单方式提交的参数, 解码是在第一次调用 request.getParamter(name) 时发生的, 通过 POST 表单方式提交的参数与 QueryString 不同, 他是通过 Http Body 传递到服务端的, 编码为 Header 中的 Content-Type, 解码也使用 Content-Type, 也可以通过 request.setCharsetEncoding(charset) 设置 (必须在第一次调用 getParamter 前). 若没有指定编码, 将使用系统默认编码进行编码, Content-Type 为空, tomcat 将使用系统默认编码进行解码.
HTTP BODY 的编解码
当用户请求的资源已经成功获取后, 这些内容将通过 Response 返回给客户端浏览器, 这个过程先要经过编码再到浏览器进行解码. 这个过程的编解码字符集可以通过 response.setCharacterEncoding 来设置, 它将会覆盖 request.getCharacterEncoding 的值, 并且通过 Header 的 Content-Type 返回客户端, 浏览器接受到返回的 socket 流时将通过 Content-Type 的 charset 来解码, 如果返回的 HTTP Header 中 Content-Type 没有设置 charset, 那么浏览器将使用 < meta HTTP-equiv="Content-Type", content="text/html; charset=utf-8"> 中的编码进行解码. 如果也没有定义的话, 那么浏览器将使用默认的编码来解码.
参考文章:
http://ilovejava.cn/posts/4a17a007/
来源: http://www.bubuko.com/infodetail-3035975.html