本文从引入jackson-dataformat-xml 之后接口全变成xml 现象开始,一步步排查代码原因,并提出解决方案。希望能对遇到相关问题的人有所帮助
新调用上游一个接口,增加了对方的一个api包,没修改任何逻辑,接口却从json返回全变成了xml格式?
原接口:
现在:
排查之后定位到是因为对方的api包里增加了一个依赖包
<dependency><groupId>com.fasterxml.jackson.dataformat</groupId><artifactId>jackson-dataformat-xml</artifactId><version>2.9.7</version>
</dependency>
那这个包是怎么生效的呢?
需要从spring-mvc 说起
当我们的代码里这么写的时候
spring-mvc 的执行流程:
SpringMVC执行流程:
1.用户发送请求至前端控制器DispatcherServlet
2.DispatcherServlet收到请求调用处理器映射器HandlerMapping。
3.处理器映射器根据请求url找到具体的处理器,生成处理器执行链HandlerExecutionChain(包括处理器对象和处理器拦截器)一并返回给DispatcherServlet。
4.DispatcherServlet根据处理器Handler获取处理器适配器HandlerAdapter执行HandlerAdapter处理一系列的操作,如:参数封装,数据格式转换,数据验证等操作
5.执行处理器Handler(Controller,也叫页面控制器)。
6.Handler执行完成返回ModelAndView
7.HandlerAdapter将Handler执行结果ModelAndView返回到DispatcherServlet
8.DispatcherServlet将ModelAndView传给ViewReslover视图解析器
9.ViewReslover解析后返回具体View
10.DispatcherServlet对View进行渲染视图(即将模型数据model填充至视图中)。
11.DispatcherServlet响应用户。
其中第9步骤里,ViewReslover解析后返回具体View 。
这里在找对应的解析器的时候有这样一步 第381行:
我们看到在getDefaultMediaTypes() 这里
就恍然大悟了。spring-webmvc里 已经有写好的判断是否有当前类 如果有作为一个可处理的类型放在map里,而恰好xml 在json之前
那么有什么办法 避免引入这个包之后就变成了xml么?如果能排除当然可以排除这个包,但是仍然无法避免下次其他依赖包里是否有对应的依赖,并且也无法排除你的项目里真的不会遇到这个包。那么怎么样可以避免呢?
其实答案就在上边的这个图里,看下方法 writeWithMessageConvert 的逻辑
protected <T> void writeWithMessageConverters(T returnValue, MethodParameter returnType, ServletServerHttpRequest inputMessage, ServletServerHttpResponse outputMessage) throws IOException, HttpMediaTypeNotAcceptableException { Class<?> returnValueClass = returnValue.getClass(); HttpServletRequest servletRequest = inputMessage.getServletRequest();
//获取客户端Accept字段接收的content-type List<MediaType> requestedMediaTypes = getAcceptableMediaTypes(servletRequest);
//获取服务器端指定的content-type,如果@RequestMapping中的produces配置了content-type,则返回此content-type,若果没有,
则获取所有HttpMessageConverter所支持的content-type,然后通过requestedMediaTypes和producibleMediaTypes 对比,选定一个最合适的content-type作为
//selectedMediaType List<MediaType> producibleMediaTypes = getProducibleMediaTypes(servletRequest, returnValueClass); Set<MediaType> compatibleMediaTypes = new LinkedHashSet<MediaType>(); for (MediaType requestedType : requestedMediaTypes) { for (MediaType producibleType : producibleMediaTypes) { if (requestedType.isCompatibleWith(producibleType)) { compatibleMediaTypes.add(getMostSpecificMediaType(requestedType, producibleType)); } } } if (compatibleMediaTypes.isEmpty()) { throw new HttpMediaTypeNotAcceptableException(producibleMediaTypes); } List<MediaType> mediaTypes = new ArrayList<MediaType>(compatibleMediaTypes); MediaType.sortBySpecificityAndQuality(mediaTypes); MediaType selectedMediaType = null; for (MediaType mediaType : mediaTypes) { if (mediaType.isConcrete()) { selectedMediaType = mediaType; break; } else if (mediaType.equals(MediaType.ALL) || mediaType.equals(MEDIA_TYPE_APPLICATION)) { selectedMediaType = MediaType.APPLICATION_OCTET_STREAM; break; } } if (selectedMediaType != null) { selectedMediaType = selectedMediaType.removeQualityValue(); for (HttpMessageConverter<?> messageConverter :
//遍历所有已注册的HttpMessageConverter,选出一个支持返回值类型returnValueClass和
//selectedMediaType的HttpMessageConverter来进行写入数据到response的body中。
this.messageConverters) { if (messageConverter.canWrite(returnValueClass, selectedMediaType)) { ((HttpMessageConverter<T>) messageConverter).write(returnValue, selectedMediaType, outputMessage); if (logger.isDebugEnabled()) { logger.debug("Written [" + returnValue + "] as \"" + selectedMediaType + "\" using [" + messageConverter + "]"); } return; } } } throw new HttpMediaTypeNotAcceptableException(this.allSupportedMediaTypes); }
所以 比较安全的解决办法是:
1) 提供方法时,我们可以指定方法的返回类型
例如: @RequestMapping(value ="/test",produces=“application/json”) 中 produces 可以指定方法对应的类型等
2) 请求方法时,我们可以指定想要的返回类型
例如: