首页 > 代码库 > spring mvc @ResponseStatus 注解 注释返回中文乱码的问题

spring mvc @ResponseStatus 注解 注释返回中文乱码的问题

前言

     前文中讲到,使用@ResponseStatus注解,可以修饰一个异常类,在发生异常的时候返回指定的错误码和消息,在返回的 reason中包含中文的时候,就会出现中文乱码的问题

现象

     reason中包含中文的时候,前端返回为乱码

/**

* 自定义异常类

*

* @author Administrator

*

*/

@ResponseStatus(value = http://www.mamicode.com/HttpStatus.FORBIDDEN, reason ="没有权限")

public class TestException extends RuntimeException {

     private static final long serialVersionUID = 5759027883028274330L;

}

     调用代码

     /**

      * 测试抛出异常乱码

      *

      * @return

      */

     @RequestMapping(value = "http://www.mamicode.com/say", produces = "text/html;charset=UTF-8")

     @ResponseBody

     String say2() {

           throw new TestException();

     }

     访问 http://localhost:8080/say2  返回乱码

技术分享

原因

     通过查看spring mvc 源码发现,解析这个 注解的类为 ResponseStatusExceptionResolver 主要是在resolveResponseStatus 中解析并 调用 response sendError 方法来想客户端发送 html 格式的异常消息,产生乱码的原因是因为编码格式不匹配,而这里明显没有 调用 response.setCharacterEncoding 方法。

public class ResponseStatusExceptionResolver extends AbstractHandlerExceptionResolver implements MessageSourceAware {

     private MessageSource messageSource;

     public void setMessageSource(MessageSource messageSource) {

           this.messageSource = messageSource;

     }

     protected ModelAndView doResolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)

  {

    ResponseStatus responseStatus = (ResponseStatus)AnnotatedElementUtils.findMergedAnnotation(ex.getClass(), ResponseStatus.class);

    if (responseStatus != null);

    try {

      return resolveResponseStatus(responseStatus, request, response, handler, ex);

    }

    catch (Exception resolveEx) {

      this.logger.warn("Handling of @ResponseStatus resulted in Exception", resolveEx);

      break label81:

      if (ex.getCause() instanceof Exception) {

        ex = (Exception)ex.getCause();

        return doResolveException(request, response, handler, ex); }

    }

    label81: return null;

  }

     protected ModelAndView resolveResponseStatus(ResponseStatus responseStatus, HttpServletRequest request,

                HttpServletResponse response, Object handler, Exception ex) throws Exception {

           int statusCode = responseStatus.code().value();

           String reason = responseStatus.reason();

           if (this.messageSource != null) {

                reason = this.messageSource.getMessage(reason, null, reason, LocaleContextHolder.getLocale());

           }

           if (!(StringUtils.hasLength(reason))) {

                response.sendError(statusCode);

           } else {

                response.sendError(statusCode, reason);

           }

           return new ModelAndView();

     }

}

spring 是通过 CharacterEncodingFilter来设置 request 和 response 的编码格式的,查看代码如下,走到这一步,就要查看 spirng boot 是在哪里定义这个过滤器的

@Override

     protected void doFilterInternal(

                HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)

                throws ServletException, IOException {

           if (this.encoding != null && (this.forceEncoding || request.getCharacterEncoding() == null)) {

                request.setCharacterEncoding(this.encoding);

                if (this.forceEncoding) {

                     response.setCharacterEncoding(this.encoding);

                }

           }

           filterChain.doFilter(request, response);

     }

查看代码,是在 HttpEncodingAutoConfiguration 这个配置类中设置的,代码如下 ,这里就用到一个属性类 HttpEncodingProperties 查看代码,调用的是 shouldForce 方法,所以只需一个设置,就可以让他强制设置编码格式 spring.http.encoding.force=true

public class HttpEncodingAutoConfiguration {

     private final HttpEncodingProperties properties;

     public HttpEncodingAutoConfiguration(HttpEncodingProperties properties) {

           this.properties = properties;

     }

     @Bean

     @ConditionalOnMissingBean({ CharacterEncodingFilter.class })

     public CharacterEncodingFilter characterEncodingFilter() {

           CharacterEncodingFilter filter = new OrderedCharacterEncodingFilter();

          filter.setEncoding(this.properties.getCharset().name());

          filter.setForceRequestEncoding(this.properties.shouldForce(HttpEncodingProperties.Type.REQUEST));

          filter.setForceResponseEncoding(this.properties.shouldForce(HttpEncodingProperties.Type.RESPONSE));

           return filter;

     }

     @Bean

     public LocaleCharsetMappingsCustomizer localeCharsetMappingsCustomizer() {

           return new LocaleCharsetMappingsCustomizer(this.properties);

     }

     private static class LocaleCharsetMappingsCustomizer implements EmbeddedServletContainerCustomizer, Ordered {

           private final HttpEncodingProperties properties;

          LocaleCharsetMappingsCustomizer(HttpEncodingProperties properties) {

                this.properties = properties;

           }

           public void customize(ConfigurableEmbeddedServletContainer container) {

                if (this.properties.getMapping() != null)

                     container.setLocaleCharsetMappings(this.properties.getMapping());

           }

           public int getOrder() {

                return 0;

           }

     }

}

boolean shouldForce(Type type) {

           Boolean force = (type == Type.REQUEST) ? this.forceRequest : this.forceResponse;

           if (force == null) {

                force = this.force;

           }

           if (force == null) {

                force = Boolean.valueOf(type == Type.REQUEST);

           }

           return force.booleanValue();

     }

解决办法


技术分享

spring.http.encoding.force=true

结果

再次访问刷新,显示正常。

技术分享

spring mvc @ResponseStatus 注解 注释返回中文乱码的问题