一次http请求涉及的编解码

java 接入rfid信息 javaweb如何获取rfid信息_HTTP

URL的编解码

URL的组成部分

java 接入rfid信息 javaweb如何获取rfid信息_java 接入rfid信息_02

以Tomcat服务器为例子,其中Port,ContextPath在Server.xml中配置

ServletPath在Web应用的web.xml中的中配置,PathInfo 是我们请求的具体的 Servlet,QueryString 是要传递的参数,注意这里是在浏览器里直接输入 URL 所以是通过 Get 方法请求的,如果是 POST 方法请求的话,QueryString 将通过表单方式提交到服务器端。

java 接入rfid信息 javaweb如何获取rfid信息_java 接入rfid信息_03

通过浏览器的请求过程,我们可知两个“君山”的编码结果是不同的: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 两个参数。如下图:

java 接入rfid信息 javaweb如何获取rfid信息_表单_04

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,那么浏览器将使用中的编码进行解码。如果也没有定义的话,那么浏览器将使用默认的编码来解码。