Java中的HTTP头编码/解码

自定义HTTP标头正在传递给Servlet应用程序以进行身份​​validation。 标头值必须能够包含重音和其他非ASCII字符,因此必须采用特定编码(理想情况下为UTF-8)。

控制身份validation环境的开发人员向我提供了这段Java代码:

String firstName = request.getHeader("my-custom-header"); String decodedFirstName = new String(firstName.getBytes(),"UTF-8"); 

但是这段代码对我来说看起来并不合适:它预先假定了头值的编码,当我觉得有一种正确的方法来指定头值的编码时(我认为来自MIME)。

这是我的问题:处理需要支持UTF-8编码的自定义标头值的正确方法(tm)是什么:

  • 在电线上(标题在电线上的样子)
  • 从解码的角度来看(如何使用Java Servlet API解码它,我们可以假设request.getHeader()已经正确地解码了)

这是一个与环境无关的代码示例,用于将标头视为UTF-8,以防您无法更改服务:

 String valueAsISO = request.getHeader("my-custom-header"); String valueAsUTF8 = new String(firstName.getBytes("ISO8859-1"),"UTF-8"); 

再说一次:RFC 2047在实践中没有实现。 HTTP / 1.1的下一个版本将删除任何提及它。

因此,如果您需要传输非ASCII字符,最安全的方法是将它们编码为ASCII序列,例如Atom发布协议中的“Slug”标头。

HTTPbis工作组意识到了这个问题,并且最新的草案摆脱了与TEXT和RFC 2047编码相关的所有语言 – 它实际上并未在HTTP上使用。

有关整个故事,请参见http://trac.tools.ietf.org/wg/httpbis/trac/ticket/74 。

有关规则,请参阅HTTP规范 ,如2.2节所述

TEXT规则仅用于描述性字段内容和不打算由消息解析器解释的值。 * TEXT的字只有在符合RFC 2047 [14]的规则编码时才包含ISO-8859-1 [22]以外的字符集中的字符。

上面的代码将无法正确解码RFC2047编码字符串,导致我认为该服务没有正确遵循规范,他们只是在头文件中嵌入原始utf-8数据。

如前所述,第一眼看起来应该始终遵循HTTP 1.1规范 (RFC 2616)。 它表示如果头文件中的文本包含来自ISO-8859-1以外的字符集的字符,则头文件中的文本必须使用定义的RFC 2047中的MIME编码。

所以这对你来说是一个加分。 如果您的要求由ISO-8859-1字符集涵盖,那么您只需将字符放入请求/响应消息中即可。 否则MIME编码是唯一的选择。

只要用户代理根据这些规则将值发送到您的自定义标头,您就不必担心解码它们。 这就是Servlet API应该做的事情。


但是,有一个更基本的原因可以解释为什么你的代码片段没有按照预期的那样进行。 第一行将标头值作为Java字符串获取。 我们知道它在内部表示为UTF8,因此此时HTTP请求消息解析已经完成并完成。

下一行获取此字符串的字节数组。 由于没有指定编码(恕我直言,这个没有参数的方法很久以前就已经弃用了),所以使用当前的系统默认编码,通常不是UTF8,然后再将数组转换为UTF8编码。 Outch。

谢谢你的回答。 似乎理想的是按照RFC 2047遵循正确的HTTP头编码。线路上的UTF-8中的头部值看起来像这样:

 =?UTF-8?Q?...?= 

现在这里有趣的是:似乎Tomcat 5.5或6都没有按照RFC 2047正确解码HTTP头! Tomcat代码假设每个标头值都使用ISO-8859-1。

因此,对于Tomcat,我将通过编写一个处理头值正确解码的filter来解决这个问题。