TLDR;
如果应用程序的默认字符集是 UTF-8,则在调用控制器方法时,某些 UTF-8 标头会被编码两次。 spring MVC 中编码在哪里处理以及如何控制?
详情
REST 控制器中的标头值存在以下问题
我们使用 SSO 服务作为代理,并向请求中注入一些额外的标头。注入的值是 mail、givenname、lastname 等。
代理在标头中注入 UTF-8 编码值(我们已使用 tcpdump 和wireshark 检查)
GET /api/v1/foo/bar HTTP/1.1
Host: blabla.fr
....
Content-Type: application/json;charset=UTF-8
Accept: */*
Accept-Encoding: gzip, deflate, sdch, br
Accept-Language: fr-FR,fr;q=0.8,en-US;q=0.6,en;q=0.4
...
givenName: Gérard
我们的控制器是这样的
@RestController
@RequestMapping("/api/v1/foo")
public class FooController {
@RequestMapping("/bar")
public ResponseEntity<List<String>> bar(@RequestHeader("foo") String foo, @RequestHeader("givenname") String firstname) throws UnsupportedEncodingException {
ArrayList<String> list = Lists.newArrayList(
"FOO: " + foo,
"CHARSET: " + Charset.defaultCharset().name(),
"FOO WITH DEFAULT CHARSET: " + asHexList(foo),
"FOO WITH UTF8: " + asHexList(new String(foo.getBytes("UTF-8"))),
"INJECTED HEADER WITH DEFAULT CHARSET: " + asHexList(firstname),
"INJECTED HEADER WITH UTF8: " + asHexList(new String(firstname.getBytes("UTF-8")))
);
return ResponseEntity.ok( list );
}
}
/**
* Print a concatenated list of hexa représentation of the string's bytes
* assert asHexList(new String("é", "UTF-8"))equals("c3 e9")
* @param String s
* @return String
*/
public String asHexList(String s) {...}
我们观察到以下行为
当系统的LANG为fr_FR.ISO-8859-1时,则返回为
[
"FOO: Gérard",
"CHARSET: ISO-8859-1",
"FOO WITH DEFAULT CHARSET: 47 e9 72 61 72 64",
"FOO WITH UTF8: 47 c3 a9 72 61 72 64",
"INJECTED HEADER WITH DEFAULT CHARSET: 47 c3 a9 72 61 72 64",
"INJECTED HEADER WITH UTF8: 47 c3 83 c2 a9 72 61 72 64"
]
当 LANG 为 fr_FR.UTF-8 时,我们有
[
"FOO: Gérard",
"CHARSET: UTF-8",
"FOO WITH DEFAULT CHARSET: 47 c3 a9 72 61 72 64",
"FOO WITH UTF8: 47 c3 a9 72 61 72 64",
"INJECTED HEADER WITH DEFAULT CHARSET: 47 c3 83 c2 a9 72 61 72 64",
"INJECTED HEADER WITH UTF8: 47 c3 83 c2 a9 72 61 72 64"
]
我们可以看到
Spring MVC 中编码在哪里处理以及如何控制? Spring 中 LANG、file.enconding 和默认字符集之间的关系是什么?
我们处于嵌入 spring-web 4.2.5-RELEASE 的 1.3.3.RELEASE spring boot 中。
非常欢迎您的帮助。
我假设您正在使用 tomcat Web 服务器。因此,看一下
ByteChunk
类,它实际上将标头从 String 刷新为默认 ISO-8859-1
字符集中的字节。
/**
* Default encoding used to convert to strings. It should be UTF8, as most
* standards seem to converge, but the servlet API requires 8859_1, and this
* object is used mostly for servlets.
*/
public static final Charset DEFAULT_CHARSET = StandardCharsets.ISO_8859_1;
更糟糕的是,Spring Mvc 没有为我们提供任何机会来指定正确的字符集来编码/解码标头值。
最后,我通过提前将
UTF-8
字符串字节解码为 ISO-8859-1
格式解决了我的问题,因此稍后 ByteChunk
将正确编码它。
headers.set(HttpHeaders.CONTENT_DISPOSITION,
new String(("attachment; filename=" + attachment.filename).getBytes(StandardCharsets.UTF_8),
StandardCharsets.ISO_8859_1));