问题现象

在全局异常处理后,发现响应体返回中文的错误信息竟然乱码了,如下:

问题原因

  • Controller
@RequestMapping(value = "/user/{user_id}", method = RequestMethod.DELETE, consumes =MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
@ResponseBody
@ResponseStatus(code = HttpStatus.OK)
public String deleteUser(HttpServletRequest request, HttpServletResponse response,@PathVariable(value = "user_id") String userId) {String userName = userService.getUserName(userId);if (userName == null) {// 此处异常被全局异常处理throw new NullPointerException();}// delete userreturn "success";
}
  • 全局异常处理器ControllerAdvice
@Component
@ControllerAdvice
public class GlobalExceptionHandler {@Resourceprivate MessageProcessor messageProcessor;/*** 处理其他未被捕获的异常,返回服务器内部错误,响应500** @param request   http请求* @param exception 参数绑定异常* @return 异常响应国际化信息*/@ExceptionHandler(value = Exception.class)@ResponseBody@ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR)public String handleException(HttpServletRequest request, Exception exception) {return messageProcessor.getMessage(request, INTERNAL_ERROR_DEFAULT_MESSAGE);}
}
  • 国际化资源包
response.500.message.0001=系统错误,请稍后重试。response.500.message.0001=System error, please try again later.

问题分析

  1. 分析下原因,是因为触发了全局异常捕获,handleException()方法上标注了@ResponseBody,即返回的message会被放入响应体中返回给前端。和Controller方法不同的是,使用@RequestMapping方法指定了consumes=MediaType.APPLICATION_JSON_VALUE,即响应体会被json处理,但是@ExceptionHandler方法未指定响应体格式Content-Type,可以在postman中查看:

    由SpringMVC处理流程可知

6、返回ModelAndView之后仍然是交由HandleAdapter去处理,所以重点分析下Adapter。这里的Adapter实现类为RequestMappingHandlerAdapter,入口为handleInternal方法,调用invokeHandlerMethod()

  • handleInternal()
@Override
protected ModelAndView handleInternal(HttpServletRequest request,HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {ModelAndView mav;mav = invokeHandlerMethod(request, response, handlerMethod);return mav;
}
  • invokeHandlerMethod()
@Nullable
protected ModelAndView invokeHandlerMethod(HttpServletRequest request,HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {// 创建ServletWebRequest对象ServletWebRequest webRequest = new ServletWebRequest(request, response);try {WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);// 设置方法参数解析器if (this.argumentResolvers != null) {invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);}// 设置方法返回值处理器if (this.returnValueHandlers != null) {invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);}invocableMethod.setDataBinderFactory(binderFactory);invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);// ModelAndView容器,将上述参数设置进去并初始化相关配置ModelAndViewContainer mavContainer = new ModelAndViewContainer();mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));modelFactory.initModel(webRequest, mavContainer, invocableMethod);mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);// 异步请求处理AsyncWebRequest,不涉及已忽略// 调用此方法并处理返回值invocableMethod.invokeAndHandle(webRequest, mavContainer);return getModelAndView(mavContainer, modelFactory, webRequest);}finally {webRequest.requestCompleted();}
}
  • invokeAndHandle()
public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer          mavContainer, Object... providedArgs) throws Exception {// 调用方法获取方法返回值,如果发生异常,此时获取的是异常处理的方法的返回值Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);// 根据@ResponseStatus注解设置响应码setResponseStatus(webRequest);// 返回值为null时处理if (returnValue == null) {if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) {disableContentCachingIfNecessary(webRequest);mavContainer.setRequestHandled(true);return;}}else if (StringUtils.hasText(getResponseStatusReason())) {// 如果在@ResponseStatus注解设置reason(),则进去此处mavContainer.setRequestHandled(true);return;}mavContainer.setRequestHandled(false);Assert.state(this.returnValueHandlers != null, "No return value handlers");// returnValueHandlers处理返回值try {this.returnValueHandlers.handleReturnValue(returnValue, getReturnValueType(returnValue), mavContainer, webRequest);}catch (Exception ex) {if (logger.isTraceEnabled()) {logger.trace(formatErrorForReturnValue(returnValue), ex);}throw ex;}
}
  • handleReturnValue()
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {// 在returnHandler列表中根据supportsReturnType()方法,获取第一个支持的HandlerHandlerMethodReturnValueHandler handler = selectHandler(returnValue, returnType);if (handler == null) {throw new IllegalArgumentException("Unknown return value type: " + returnType.getParameterType().getName());}// Handler中有messageConverters列表,根据messageConverter的canWrite()方法选择合适的messageConvert,并将message写入到response中。handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
}

由上分析可知,最终是根据messageConvert将返回值写入到response中。由于返回值是String,而messageConvert所以会使用StringHttpMessageConvert:

而StringHttpMessageConvert的Content-Type默认是text/plain;charset=UTF-8,编码方式是ISO-8859-1,所以产生中文乱码

解决方法

  • 解决方法一:在配置文件中指定StringHttpMessageConverter的字符集,推荐此方案。

    <!--开启SpringMVC注解驱动-->
    <!--指定validator-->
    <mvc:annotation-driven validator="validator"><!--指定StringHttpMessageConverter的字符集--><mvc:message-converters><bean class="org.springframework.http.converter.StringHttpMessageConverter"><property name="supportedMediaTypes"><list><value>text/plain;charset=utf-8</value><value>text/html;charset=UTF-8</value></list></property></bean></mvc:message-converters>
    </mvc:annotation-driven>
    
  • 解决方法二:在bean后处理器中指定StringHttpMessageConverter的字符集,所有的被spring托管的bean都会执行postProcessAfterInitialization方法,建议使用解决方法一。

    @Component
    public class DefineCharSet implements BeanPostProcessor {//实例化之前调用@Nullablepublic Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {return bean;}//实例化之后调用@Nullablepublic Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {// 指定StringHttpMessageConverter的字符集if(bean instanceof StringHttpMessageConverter){MediaType mediaType = new MediaType("text", "html", Charset.forName("UTF-8"));List<MediaType> types = new ArrayList<MediaType>();types.add(mediaType);((StringHttpMessageConverter) bean).setSupportedMediaTypes(types);}return bean;}
    }
    

测试:

@ExceptionHandler全局异常捕获响应体返回中文乱码相关推荐

  1. 编码技巧——全局异常捕获统一的返回体业务异常

    在开发中,关于异常的捕获曾经是一个头疼的问题:本篇介绍几个方法,如何优雅的捕获处理业务异常: 已检查异常和未检查异常? 先做个介绍,异常Exception分为运行时异常(RuntimeExceptio ...

  2. springboot上传文件过大,全局异常捕获,客户端没有返回值

    springboot上传文件过大,全局异常捕获,客户端没有返回值 参考文章: (1)springboot上传文件过大,全局异常捕获,客户端没有返回值 (2)https://www.cnblogs.co ...

  3. Spring 全局异常捕获

    引言 前后端分离开发,后台有时候会出现不可预知的异常(运行时异常),在实际生产中通常需要统一返回符合一定响应结构的异常信息给前端,这一方面可以避免用户看到后台的报错信息,一方面也是保护后端程序免受恶意 ...

  4. java报错空指针异常_springboot全局异常捕获,真香

    全局异常捕获 什么是异常?程序在启动或者运行时没有按照预期的执行,在执行途中发生某种未知的错误,导致程序非正常停止或者报错. 在我们的程序中,肯定会伴随着很多的异常,启动时:空对象.找不到数据库.用户 ...

  5. spring boot: GlobalDefaultExceptionHandler方法内的友好错误提示,全局异常捕获

    spring boot: GlobalDefaultExceptionHandler方法内的友好错误提示,全局异常捕获 当你的某个控制器内的某个方法报错,基本上回显示出java错误代码,非常不友好,这 ...

  6. Java 捕获 mybatis异常_3 springboot集成mybatis和全局异常捕获

    mybatis有两种方式,一种是基于XML,一种是基于注解 springboot集成mybatis 首先先创建表,这里都简化了 DROP TABLE IF EXISTS `user`; CREATE ...

  7. 《全局异常捕获》劝劝潘子吧,别再用trycatch来处理异常了

    人世仙家本自殊,何须相见向中途.惊鸿瞥过游龙去,漫恼陈王一事无. 嗨,大家好,我是洛神,性别男.一个来自快乐星球的程序员. 欢迎大家专注我的公众号[程序员洛神],不仅分享技术,还会分享生活趣事.体育. ...

  8. SpringBoot配置全局异常捕获

    SpringBoot中自带的异常捕获机制返回的默认页面比较丑,对用户来说不够人性化.所以这篇文章来讲解SpringBoot钟自定义全局异常捕获. 本文的源码已经上传GitHub:https://git ...

  9. 全局异常捕获和自定义异常类

    在代码执行中,不可避免的会出现报错信息.后端需要将这些报错信息告诉前端,前端才可以根据报错情况来给用户展示不同的页面 而后台的报错信息大多情况写前端人员都看不太懂,所以需要后盾人员对报错信息进行处理, ...

最新文章

  1. 开平方的 7 种算法
  2. 《数据库原理与应用(第3版)》——1.4 数据库系统的组成
  3. javascript --- 防抖与节流
  4. 2.3 基本算法之递归变递推 1188 菲波那契数列(2) python
  5. 在VS2010中F5调试Silverlight程序时,提示“无法启动调试,找不到Microsoft Internet Explorer”...
  6. Linux内核调试 - 一般人儿我都不告诉他(一)
  7. PlantUML 简明教程
  8. 微信小程序图标 icon组件
  9. 计算机代数与数论pdf,计算机代数及数论(maple).pdf
  10. 计算机一级考试搜题app,计算机一级考试题库答案,哪个搜题软件能找到?
  11. 【游戏】蔚蓝与空洞骑士
  12. 期货期权各个品种详情
  13. [W806捣鼓手记]FPU性能简单测试——2022.05.23
  14. Java核心技术(进阶)
  15. P3545 [POI2012]HUR-Warehouse Store
  16. Prometheus(二)基础概念
  17. 北京大额股票配资 北京股票金融配资
  18. 详细解析浏览器加载网页的整个过程
  19. #HHD32F107# SPI通信
  20. 北京大学计算机专业王腾,王腾-青年学者-北大地空学院

热门文章

  1. RGB读取转为BGR显示
  2. PID算法(电子入门必看)手把手教学
  3. 共享型mysql_共享型汽车租赁系统(SSM+MySql)
  4. 工业智能网关BL110应用之三十四: 如何连接配置亚马逊云服务器
  5. 【华为认证】HCIA-DATACOM史上最全精选题库续(附答案解析)
  6. Android8.0、9.0安装包解析失败
  7. nginx+ssl 更改默认443端口
  8. 前端小练——CSS如何做十字图样
  9. java短信验证平台_JAVA实现利用第三方平台发送短信验证码
  10. 35岁转行,是我人生中最正确的选择