自定义 HTTP 标头被传递到 Servlet 应用程序以进行身份验证。标头值必须能够包含重音符号和其他非 ASCII 字符,因此必须采用某种编码(最好是 UTF-8)。
控制身份验证环境的开发人员向我提供了这段 Java 代码:
String firstName = request.getHeader("my-custom-header");
String decodedFirstName = new String(firstName.getBytes(),"UTF-8");
但是这段代码对我来说看起来不太正确:它预设了标头值的编码,而在我看来,有一种正确的方法来指定标头值的编码(我相信来自 MIME)。
这是我的问题:处理需要支持 UTF-8 编码的自定义标头值的正确方法 (tm) 是什么:
这里是一个独立于环境的代码示例,用于将标头视为 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 Publishing Protocol 中的“Slug”标头。
如前所述,首先应该查看 HTTP 1.1 规范 (RFC 2616)。 它表示如果标头值中的文本包含 ISO-8859-1 以外的字符集中的字符,则必须使用 RFC 2047 定义的 MIME 编码。
所以这对你来说是一个加分。如果 ISO-8859-1 字符集满足您的要求,那么您只需将字符放入请求/响应消息中即可。否则,MIME 编码是唯一的选择。
只要用户代理根据这些规则将值发送到您的自定义标头,您就不必担心解码它们。这就是 Servlet API 应该做的事情。
但是,有一个更基本的原因可以解释为什么您的代码片段没有执行其应有的操作。第一行以 Java 字符串形式获取标头值。我们知道它内部表示为 UTF8,因此此时 HTTP 请求消息解析已经完成。
下一行获取该字符串的字节数组。由于未指定编码(恕我直言,这种不带参数的方法应该早就被弃用了),因此使用当前系统默认编码,通常不是 UTF8,然后再次将数组转换为 UTF8 编码。 Outch。
HTTPbis 工作组已经意识到这个问题,最新的草案删除了所有与 TEXT 和 RFC 2047 编码有关的语言——它在 HTTP 上并未实际使用。
请参阅 http://trac.tools.ietf.org/wg/httpbis/trac/ticket/74 了解整个故事。
有关规则,请参阅 HTTP 规范,第 2.2 节中有说明
TEXT 规则仅用于不打算由消息解析器解释的描述性字段内容和值。仅当根据 RFC 2047 [14] 的规则进行编码时,*TEXT 中的单词才可以包含 ISO-8859-1 [22] 以外的字符集中的字符。
上面的代码无法正确解码 RFC2047 编码字符串,让我相信该服务没有正确遵循规范,它们只是在标头中嵌入原始 utf-8 数据。
感谢您的回答。看起来理想的情况是遵循 RFC 2047 中正确的 HTTP 标头编码。线路上的 UTF-8 标头值看起来像这样:
=?UTF-8?Q?...?=
有趣的是:Tomcat 5.5 或 6 似乎都无法按照 RFC 2047 正确解码 HTTP 标头! Tomcat 代码假定标头值在任何地方都使用 ISO-8859-1。
因此,对于 Tomcat,具体来说,我将通过编写一个过滤器来解决这个问题,该过滤器可以处理标头值的正确解码。