首页>>后端>>SpringBoot->SpringBoot支持application/xml及406异常分析

SpringBoot支持application/xml及406异常分析

时间:2023-11-30 本站 点击:0

背景

在 Spring 框架中,@ResponseBody 注解响应的对象会利用 HttpMessageConverter 类,根据 produces 属性指定的类型,找到对应的数据转换器完成转换。

最常见也是默认的响应类型是 application/json,还有很多其他的响应数据类型,都封装在 org.springframework.http.MediaType 中了,本文将介绍 application/xml 类型的响应支持及其引发的思考。

支持 application/xml

定义一个请求方法,设置响应类型为 XML:

@ResponseBody@RequestMapping(value="/hello/json")publicMyDatahello(){returnnewMyData("13020045218","10");}@ResponseBody@RequestMapping(value="/hello/xml",produces=MediaType.APPLICATION_XML_VALUE)publicMyDatahello1(){returnnewMyData("13020045218","10");}

关键配置

依赖引入

<dependency><groupId>com.fasterxml.jackson.dataformat</groupId><artifactId>jackson-dataformat-xml</artifactId><version>2.9.0</version></dependency>

如果不添加上述依赖,而又在 Controller 的方法上指定了 XML 类型,服务端会抛出 406 异常:

异常类型为:

org.springframework.web.HttpMediaTypeNotAcceptableException:Couldnotfindacceptablerepresentation

加入依赖后,页面返回 XML 数据:

延伸思考两个知识点

框架用久了,难免会忘记它底层的封装过程,像本文这个简单的练习,源于 SpringBoot 中重新设置 MappingJackson2HttpMessageConverter 以解决特殊转换问题,同时针对 XML 类型中的特殊转换,需要重新设置 MappingJackson2XMLHttpMessageConverter

思考两个问题:

406 是什么原因导致的?

这个引用包中是怎么让 XML 转换器自动加入到 Spring 环境中的?

第一个问题容易猜测:请求指定响应数据格式为 application/xml 后,系统在返回时会调用org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor#handleReturnValue,这个里面会使用 MessageConverter,查找 XML 类型的转换器。出现 406 错误,说明没有找到该转换器。

第二个问题,SpringBoot 自动装配 Jackson 转换器是通过 JacksonHttpMessageConvertersConfiguration 类完成的,它会注入 JSON 转换器 MappingJackson2HttpMessageConverter 和 XML 的转换器 ,它的源码如下:

@org.springframework.context.annotation.ConfigurationclassJacksonHttpMessageConvertersConfiguration{JacksonHttpMessageConvertersConfiguration(){/*compiledcode*/}@org.springframework.context.annotation.Configuration@org.springframework.boot.autoconfigure.condition.ConditionalOnClass({com.fasterxml.jackson.dataformat.xml.XmlMapper.class})@org.springframework.boot.autoconfigure.condition.ConditionalOnBean({org.springframework.http.converter.json.Jackson2ObjectMapperBuilder.class})protectedstaticclassMappingJackson2XmlHttpMessageConverterConfiguration{protectedMappingJackson2XmlHttpMessageConverterConfiguration(){/*compiledcode*/}@org.springframework.context.annotation.Bean@org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBeanpublicorg.springframework.http.converter.xml.MappingJackson2XmlHttpMessageConvertermappingJackson2XmlHttpMessageConverter(org.springframework.http.converter.json.Jackson2ObjectMapperBuilderbuilder){/*compiledcode*/}}@org.springframework.context.annotation.Configuration@org.springframework.boot.autoconfigure.condition.ConditionalOnClass({com.fasterxml.jackson.databind.ObjectMapper.class})@org.springframework.boot.autoconfigure.condition.ConditionalOnBean({com.fasterxml.jackson.databind.ObjectMapper.class})@org.springframework.boot.autoconfigure.condition.ConditionalOnProperty(name={"spring.http.converters.preferred-json-mapper"},havingValue="jackson",matchIfMissing=true)protectedstaticclassMappingJackson2HttpMessageConverterConfiguration{protectedMappingJackson2HttpMessageConverterConfiguration(){/*compiledcode*/}@org.springframework.context.annotation.Bean@org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean(value={org.springframework.http.converter.json.MappingJackson2HttpMessageConverter.class},ignoredType={"org.springframework.hateoas.mvc.TypeConstrainedMappingJackson2HttpMessageConverter","org.springframework.data.rest.webmvc.alps.AlpsJsonHttpMessageConverter"})publicorg.springframework.http.converter.json.MappingJackson2HttpMessageConvertermappingJackson2HttpMessageConverter(com.fasterxml.jackson.databind.ObjectMapperobjectMapper){/*compiledcode*/}}}

注意第一部分, XML 转换器的自动注入条件是:@org.springframework.boot.autoconfigure.condition.ConditionalOnClass({com.fasterxml.jackson.dataformat.xml.XmlMapper.class}) 这就是引入依赖自动触发注入的根源了。

这个条件注入又引发了第三个问题,不引入 jar 包,这个配置就存在编译错误,怎么解决这个编译错误的呢?

搜索到一篇《注解@ConditionalOnClass(X.class),X不存在时的探究》,原理类似。

猜测同理, spring-boot 引用了这个包,但是 optional=true ,所以依赖不会传递到我们项目中,如果不引入相关 jar 又想用 @ConditionOnClass 的话,可以使用 name 属性:

@ConditionalOnClass(name="com.fasterxml.jackson.dataformat.xml.XmlMapper")

启示录

以上就是 Jackson 转换器引发的思考:

如何更改 MappingJackson2HttpMessageConverter 默认配置?

@ConditionalOnClass 未引入对应 jar 包时,编译错误是怎么回事?

SpringBoot 是如何自动装配 Jackson 的两个转换器的?

看完本文,你明白怎么回事儿了吗?


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:/SpringBoot/4372.html