[关闭]
@marlin 2018-02-28T05:01:00.000000Z 字数 2960 阅读 1706

从“前后端接口乱码问题”说起

乱码 postman 接口 调试 dispatch


在进行后端调试时通常会遇到编码问题。比如:后端配置返回信息时通过json可以正常显示中文,可是通过xml中文就会乱码。在这篇文里将介绍如下几个要点:1.摘要介绍spring mvc的dispatch流程;2.介绍http请求中的Content-Type;3.不同格式数据之间的转换;4.对Exception做处理;5.对异常与数据做统一包装。

spring mvc的dispatch流程

为什么说编码要先介绍dispatch流程?这主要是由于只有知道dispatch流程--这样一个大背景(background)--才能明确数据格式转换在什么情况下发生,又怎样实现转换,才能知道乱码出现在哪个阶段。

流程示意图

一个请求(url)来到服务器会经历一系列判断,在这里我们重点关注两个位置:1.调用目标Handler的目标方法得到ModelAndView对象;2.由HandlerExceptionResolver组件处理异常,得到新的ModelAndView对象。这两个位置构成了返回数据的两个来源,在没异常的情况下,由action中的方法返回数据;存在异常时将异常包装成新的数据返回。

这里又不得不提到spring mvc中controller控制器的返回值,我们知道一个http请求可以返回ModelAndView,也可以跳转到一个新的页面,更可以收到数据。在这里这里比较详细地介绍了controller的7种返回值:ModelAndView、Model、ModelMap、Map、View、String、Void。
在spring mvc早期设计中,并没有意识到ajax这种通过接口传输数据方式的广泛适用性,因此controller的设计更多的是Model、View,其中Model是返回的数据对象,而View则是装载这些对象的容器(用于渲染)。为了兼容直接返回数据这种情况,在Spring 3.X系列新增了@ResponseBody注解,用于实现“将内容或对象作为 HTTP 响应正文返回”。有关@ResponseBody的简要介绍参考这里
单纯的数据http请求(ajax),在服务端需要配合@ResponseBody来实现,并结合两种返回数据的场景进行处理。

Content-Type

这里是一篇详细介绍Content-Type的文章。
重点说明下,浏览器通过Accept头告知服务器可以以哪种类型将数据包装并返回,比如:“Accept=application/json”就是告知服务器请产生json格式的数据。当然,浏览器可以根据实际情况顺序声明多种可以接受的数据类型。
在服务器接收到http请求后,1.会解析Accept头,获得浏览器需要的数据类型;2.查看该请求在服务端的返回值,并取得相应的数据类型;3.完成兼容性检查后利用数据转换器(converter)转换成相应类型的数据。
因此合理设置服务端的相应配置,可以更大程度地兼容浏览器的多样请求。

服务端应对数据转换

针对在没异常的情况下,由action中的方法返回数据这种情况,spring已经做了各种兼容性处理,主要是AbstractMessageConverterMethodArgumentResolver中已经初始化了多种可用的HttpMessageConverter,即messageConverters(是一个converter数组)。在进行处理时最终会调用writeWithMessageConverters方法完成数据类型转换,具体的签名和代码片段如下

  1. protected <T> void writeWithMessageConverters(T returnValue, MethodParameter returnType, ServletServerHttpRequest inputMessage, ServletServerHttpResponse outputMessage) throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {
  2. //...省略代码
  3. for (HttpMessageConverter<?> messageConverter : this.messageConverters) {
  4. //省略判断
  5. returnValue = (T) getAdvice().beforeBodyWrite(returnValue, returnType, selectedMediaType, (Class<? extends HttpMessageConverter<?>>) messageConverter.getClass(), inputMessage, outputMessage);
  6. //...省略代码
  7. return;
  8. }
  9. //省略代码
  10. }

在spring中,通过配置机制已经很好地处理了多种类型间数据转换的兼容性问题,也很好地处理了中文字符的问题。
但在存在异常时将异常包装成新的数据返回这种情况下,并没有提供完整的数据转换机制。

对Exception做处理

spring在默认情况下提供了异常的包装,主要提供了三种机制:1.使用SimpleMappingExceptionResolver,这可以通过配置默认实现;2.实现HandlerExceptionResolver接口,实现自定义处理异常;3.通过@ExceptionHandler注解,在代码中对异常做出处理。详细可以参考这里

通常来说在开发过程中,如果兼顾到系统的整体规划应该使用@ExceptionHandler注解的形式对数据进行处理。尤其在提供单纯的ajax服务时,不应把返回数据与jsp绑定在一起,更不应该针对4XX或5XX返回一张图片或一个页面。

将异常信息与正常数据统一

考虑到ajax请求在数据格式上的一致性,因此有必要将调用接口时产生的异常信息与正常的结果值封装成统一的格式进行返回,这样有助于前端进行必要的判断,而不是对多种情况分别处理。
所谓统一方法,就是分别对正常返回数据和异常信息进行包装,1.给出包装正常返回数据的实现思路:自定义的ServletInvocableHandlerMethod类,重写invokeForRequest方法,就可以对returnValue进行自定义包装了。2.给出包装异常信息的实现思路:自定义DispatcherServlet类,重写processHandlerException方法,就可以进行封装处理了。
由于正常数据可以由AbstractMessageConverterMethodArgumentResolver类中的messageConverters完成转换,而异常数据包装后的对象却没有可以处理的方法,因此需要自定义地实现转换方法。

此处的实现思路来自于 @吴伟 同学。

由于Spring在实现converters时使用了配置机制,扩展其兼容性。在封装异常数据时进行数据转换需要小心处理,否则就容易造成中文乱码现象。

开发调试工具推荐:Postman,其口号是:Postman Makes API Development Simple.

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注