Spring MVC框架下406错误解析
报错如下:
这里产生的场景一般是你使用@ResponseBody,希望把后台的model转换成其他的格式如(json/xml)返回给前台。下面我们以返回json格式为例,浅析一下原理,然后分析一下可能出现的问题以及处理方法。
原理浅析
1. 注册消息转换器MappingJackson2HttpMessageConverter
我们都知道,spring mvc会自动帮我们注册消息转换器MappingJackson2HttpMessageConverter用来把model返回成json,注册是由类AllEncompassingFormHttpMessageConverter完成的,其构造函数如下:
public AllEncompassingFormHttpMessageConverter() {
addPartConverter(new SourceHttpMessageConverter<Source>());
if (jaxb2Present && !jackson2XmlPresent) {
addPartConverter(new Jaxb2RootElementHttpMessageConverter());
}
if (jackson2Present) {
addPartConverter(new MappingJackson2HttpMessageConverter());
}
else if (gsonPresent) {
addPartConverter(new GsonHttpMessageConverter());
}
if (jackson2XmlPresent) {
addPartConverter(new MappingJackson2XmlHttpMessageConverter());
}
}
通过构造函数我们可以看出,只要jackson2Present为true,spring就会自动帮我们注册MappingJackson2HttpMessageConverter,我们再来看一下jackson2Present,
private static final boolean jackson2Present =
ClassUtils.isPresent("com.fasterxml.jackson.databind.ObjectMapper", AllEncompassingFormHttpMessageConverter.class.getClassLoader()) &&
ClassUtils.isPresent("com.fasterxml.jackson.core.JsonGenerator", AllEncompassingFormHttpMessageConverter.class.getClassLoader());
只要类加载器中有ObjectMapper,JsonGenerator这两个类,jackson2Present 就会为true。从这里,我们看到,一定要引入jackson的jar包才能注册MappingJackson2HttpMessageConverter。
2. AllEncompassingFormHttpMessageConverter初始化
从刚才的分析中我们知道了AllEncompassingFormHttpMessageConverter初始化的时候会注册消息转换器,那么AllEncompassingFormHttpMessageConverter初始化又发生在什么地方呢?
通过搜索,我们得知,AllEncompassingFormHttpMessageConverter发生的地方有很多,在这里只能是RequestMappingHandlerAdapter中,其构造函数如下:
public RequestMappingHandlerAdapter() {
StringHttpMessageConverter stringHttpMessageConverter = new StringHttpMessageConverter();
stringHttpMessageConverter.setWriteAcceptCharset(false); // see SPR-7316
this.messageConverters = new ArrayList<HttpMessageConverter<?>>(4);
this.messageConverters.add(new ByteArrayHttpMessageConverter());
this.messageConverters.add(stringHttpMessageConverter);
this.messageConverters.add(new SourceHttpMessageConverter<Source>());
this.messageConverters.add(new AllEncompassingFormHttpMessageConverter());
}
很明显,在构造函数中初始化了AllEncompassingFormHttpMessageConverter,我们都知道RequestMappingHandlerAdapter 在使用了annotation-driven后自动注册。 所以,这就要求我们在dispatcherServlet的配置文件中不要忘了<mvc:annotation-driven />
。
错误排查
通过分析上面的原因,错误的地方已经很容找到了。
1. 检查dispatcherServlet的配置文件 dispatcher-servlet.xml
一定要有<mvc:annotation-driven />
并且注意检查命名空间是否配置错误,命名空间里要有对应的mvc配置
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.0.xsd
**http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd**">
2. 检查有没有引入jackson相关的包
由于maven是自动依赖,在pom.xml中只需要引入jackson-databind即可
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.8.9</version>
</dependency>
3. 有没有在controller方法返回体上使用@ResponseBody注解
@ResponseBody
public UserBean getUser() {
UserBean userBean = userService.getUser(1);
return userBean;
}