Java 中的 HTTP 标头编码/解码

2022-09-04 01:02:23

自定义 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) 是什么:

  • 在导线上(接头在导线上的样子)
  • 从解码的角度来看(如何使用Java Servlet API对其进行解码,我们可以假设request.getHeader()已经正确地进行了解码)

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

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

答案 1

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

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


答案 2

如前所述,第一个外观应始终转到HTTP 1.1规范(RFC 2616)。它说标头值中的文本必须使用定义的 RFC 2047 中的 MIME 编码,如果它包含 ISO-8859-1 以外的字符集的字符。

所以这里有一个加分项给你。如果 ISO-8859-1 字符集满足您的要求,那么您只需将字符放入请求/响应消息中即可。否则,MIME 编码是唯一的替代方法。

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


但是,有一个更基本的原因,为什么你的代码片段没有做它应该做的事情。第一行将标头值提取为 Java 字符串。众所周知,它在内部表示为 UTF8,因此此时 HTTP 请求消息解析已经完成并完成。

下一行提取此字符串的字节数组。由于没有指定编码(恕我直言,这种没有参数的方法早就应该被弃用),因此使用当前的系统默认编码,这通常不是UTF8,然后数组再次转换为UTF8编码。出局。