SpringMVC中Controller處理response.setContentType()及header爲"application/json;charset=utf-8"無效問題分析
一. 異常問題
我在SpringMVC中創建了一個處理器Controller,處理器方法內部利用response對象設置content-type,內容類型設置爲“application/json;charset=UTF-8”,在該方法對外輸出中文時,結果發現還是亂碼。代碼如下:
@ResponseBody
@RequestMapping(value = "/error",method = RequestMethod.GET)
public String showError(HttpServletResponse response) throws IOException {
//專門用來處理通過註解方式實現的處理器的!
//RequestMappingHandlerMapping
//RequestMappingHandlerAdapter--->invokeHandlerMethod()
response.setContentType("application/json;charset=UTF-8");
return "失敗";
}
結果在請求時,發現依然亂碼:
二. 原因分析
後來經過追蹤源碼,發現是因爲SpringMVC源碼內部做了特殊處理,導致通過response.setContentType("application/json;charset=UTF-8")方法設置請求頭編碼無效。
SpringMVC中是通過RequestMappingHandlerAdapter來處理通過註解方式定義的請求處理器方法的,然後在該類中,有一個invokeHandlerMethod()方法,內部通過ServletInvocableHandlerMethod中的invokeAndHandle調用目標方法,並處理返回值。
如果return value != null,則通過returnvalueHandlers來處理,內部會調用MessageConverter轉換成相應的報文格式。
HttpOutputMessage中的outputMessage,對應的實例是 org.springframework.http.server.ServletServerHttpResponse。在寫入數據的同時,會設置response的header,包括content-type(根據RequestMapping 的 produces 屬性 計算出來)。也就是說,當請求方法在帶有返回值的情況下,在controller中設置content-type是無效的,會被消息轉換器覆蓋掉。
三.解決辦法
1.解決方式一,利用produces屬性
@ResponseBody
//@GetMapping("/show")
@RequestMapping(value = "/show",method = RequestMethod.GET,produces = {"application/json;charset=UTF-8"})
public String showCharacter(){
return "成功";
}
2. 解決方式二,請求方法不要帶返回值
@ResponseBody
@RequestMapping(value = "/error",method = RequestMethod.GET)
public void showError(HttpServletResponse response) throws IOException {
//
//專門用來處理通過註解方式實現的處理器的!
//RequestMappingHandlerMapping
//RequestMappingHandlerAdapter--->invokeHandlerMethod()
//AbstractHttpMessageConverter
response.setContentType("application/json;charset=UTF-8");
ServletOutputStream out = response.getOutputStream();
IOUtils.write("失敗".getBytes("UTF-8"),out);
//IOUtils.write("失敗",out);
out.flush();
//return "失敗";
}