What Happened When Configure ContentNegotiationManager
基于Spring Boot的后台rest接口工程,通过使用EnableWebMvc注解可以开启Spring MVC支持,并且通过扩展WebMvcConfigurer或其抽象子类WebMvcConfigurerAdapter,覆写其configureContentNegotiation方法可以配置Spring MVC框架在确定请求对应媒体类型的行为,而Spring MVC框架在判断一个请求对应的媒体类型时,是通过注册一个ContentNegotiationManager类型的Bean,所有的判断行为逻辑都在这个类里面。而这个类其实是将实际的业务逻辑代理给了它的成员变量strategies。源代码如下:
|
|
其中resolveMediaTypes方法即为处理逻辑方法,可以看到业务逻辑的实际处理逻辑在strategies中的各个strategy中。而WebMvcConfigurer类的configureContentNegotiation方法实际就是添加了一些配置用来改变产生ContentNegotiationManager类Bean时配置其strategies的行为,此方法的参数为ContentNegotiationConfigurer类型,通过查看ContentNegotiationConfigurer的源代码我们可以看其实际将配置参数传递给了成员变量factory,类型为ContentNegotiationManagerFactoryBean,所以实际最终产生ContentNegotiationManager的代码都在ContentNegotiationManagerFactoryBean里面,源代码如下:
|
|
从源代码可以看到产生contentNegotiationManager的代码逻辑在afterPropertiesSet方法中。至此在spring boot中通过EnableWebMvc和扩展WebMvcConfigurerAdapter配置contentNegotiation逻辑就梳理完成了。
Could not parse header json?
在项目实际运行的时候,spring mvc框架报错了,查看异常栈发现是在ContentNegotiationStrategy的一个实现类HeaderContentNegotiationStrategy的resolveMediaTypes方法中报错。源代码如下:
|
|
从源代码中我们可以看到,此类是通过请求中的Accept头部来判断客户端请求是要获取什么类型的数据,而从异常栈中可以看到异常只可能来自于MediaType.parseMediaType这一行,往下追踪源代码,发现异常来自于MimeTypeUtils类,代码如下:
|
|
至此我们发现,原来是因为Accept头部json没有包含/。
How To Fix?
通过上面的分析我们发现,问题的源头在于contentNegotiationManager的strategies中包含了HeaderContentNegotiationStrategy,我们要做的就是配置ContentNegotiationManagerFactoryBean,使其在产生contentNegotiationManager时不要配置HeaderContentNegotiationStrategy,查看ContentNegotiationManagerFactoryBean源代码可以看到,通过配置其ignoreAcceptHeader变量为true即可,这个配置项也可通过WebMvcConfigurerAdapter的configureContentNegotiation进行间接配置,代码如下:
|
|