【SpringMVC】根据请求处理资源表述
通常一个web应用中,资源都在浏览器中以HTML的形式表述。
但对于一个RESTful的资源应以多种形式表述,用户要求什么就以什么样的形式表述。
比如HTML、XML、JSON、PDF、EXCEL...
Spring有两种方式处理表述形式:
·org.springframework.web.servlet.view.ContentNegotiatingViewResolver
·org.springframework.http.converter.HttpMessageConverter
一、ContentNegotiatingViewResolver
虽然也是ViewResolver一族的,但他并不解析视图,而是解析视图的任务委托给其他ViewResolver。
默认情况下会从所有ViewResolver中自动选择合适的ViewResolver,但为了更明确的表述,我们会选择使用setViewResolvers(List<ViewResolver> viewResolvers)更明确地定义ViewResolver。
ContentNegotiatingViewResolver委托所有viewResolvers解析视图后将视图存放在待选视图列表(candidateViews)中,如果设置了defaultView,默认视图会在列表的尾部。
如何确定请求的媒体类型? 虽然accept header里也可以获得请求的表述形式,但ContentNegotiatingViewResolver会先考虑URL后面的扩展名。
从扩展名无法得到类型时将考虑accept header。如果此时仍然无法获得有效的类型,将使用defaultContentType设置(先已在ContentNegotiatingViewResolver中deprecated,可以在org.springframework.web.accept.ContentNegotiationManagerFactoryBean中看到)。
我们可以自定义扩展名和媒体类型映射。
看一些书和一些博客直接使用了ContentNegotiatingViewResolver的setMediaTypes(Map<String, String> mediaTypes),但值得注意的是,这个方法已经deprecated了(当然,与之有关的一系列setFavorPathExtension、setFavorParameter、setIgnoreAcceptHeader什么的都deprecated了,但他们都移到了ContentNegotiationManager中)。
现在建议使用setContentNegotiationManager(ContentNegotiationManager contentNegotiationManager),但并不复杂,只是变得更加OO而已。
另外org.springframework.web.accept.ContentNegotiationManagerFactoryBean是ContentNegotiationManager的:
A factory providing convenient access to a {@code ContentNegotiationManager} configured with one or more {@link ContentNegotiationStrategy} instances.
配置contentNegotiationMananger:
<bean id="contentNegotiationManager"
class="org.springframework.web.accept.ContentNegotiationManagerFactoryBean">
<property name="favorParameter" value="true" />
<property name="parameterName" value="format" />
<property name="ignoreAcceptHeader" value="false" />
<property name="mediaTypes">
<value>
json=application/json
xml=application/xml
</value>
</property>
<property name="defaultContentType" value="text/html" />
</bean>话说favorPathExtension默认就是true,因此在这里就不做设置了。
favorPathExtension为false则勿略扩展名。
favorParameter与parameterName是一对属性。
favorParameter设置为true可以用[?format=表述形式]这种方式。
默认值是format,这个值可以通过parameterName来设置。
ignoreAcceptHeader默认为false,即不忽略Accept信息。
配置ContentNegotiatingViewResolver:
<bean class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver">
<property name= "contentNegotiationManager" ref= "contentNegotiationManager"/>
<!-- <property name="mediaTypes"> -->
<!-- <map> -->
<!-- <entry key="atom" value="application/atom+xml" /> -->
<!-- <entry key="html" value="text/html" /> -->
<!-- <entry key="json" value="application/json" /> -->
<!-- </map> -->
<!-- </property> -->
<property name="viewResolvers">
<list>
<!-- <bean class="org.springframework.web.servlet.view.BeanNameViewResolver" /> -->
<bean
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/views/" />
<property name="suffix" value=".jsp" />
</bean>
</list>
</property>
<property name="defaultViews">
<list>
<bean class="org.springframework.web.servlet.view.json.MappingJacksonJsonView" />
</list>
</property>
</bean>ContentNegotiatingViewResolver的setMediaTypes没deprecated时是想上面那样写的,现在都写到contentNegotiationMananger了。
二、HttpMessageConverter
通常可以看到把对象放到Model中在View中渲染。使用HttpMessageConverter则可以将方法返回的对象转为要求的表述形式。
上面是实现类,很多都是自动注册的。
见org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport
protected final void addDefaultHttpMessageConverters(List<HttpMessageConverter<?>> messageConverters) {
StringHttpMessageConverter stringConverter = new StringHttpMessageConverter();
stringConverter.setWriteAcceptCharset(false);
messageConverters.add(new ByteArrayHttpMessageConverter());
messageConverters.add(stringConverter);
messageConverters.add(new ResourceHttpMessageConverter());
messageConverters.add(new SourceHttpMessageConverter<Source>());
messageConverters.add(new AllEncompassingFormHttpMessageConverter());
if (romePresent) {
messageConverters.add(new AtomFeedHttpMessageConverter());
messageConverters.add(new RssChannelHttpMessageConverter());
}
if (jaxb2Present) {
messageConverters.add(new Jaxb2RootElementHttpMessageConverter());
}
if (jackson2Present) {
messageConverters.add(new MappingJackson2HttpMessageConverter());
}
else if (jacksonPresent) {
messageConverters.add(new org.springframework.http.converter.json.MappingJacksonHttpMessageConverter());
}
}另外我们需要用到@ResponseBody,以表示Controller的返回值会绑定在响应体中,并将其转换为客户端请求的表述形式。
请求的表述形式主要是Accept头信息,如果请求中不包括Accept头信息则会假设可以接受任何表述形式。当然,如果在方法的@RequestMapping中设置了Accept头信息则只会处理包含这些Accept信息的请求。
比如下面的代码中我要将User数组转为JSON形式表述:
@RequestMapping(value ="/userList",headers={"Accept="+MediaType.APPLICATION_JSON_VALUE})
public @ResponseBody User[] queryUserList(){
User[] u = new User[4];
u[0] = new User("0001","King","t;stmdtkg");
u[1] = new User("0001","King","t;stmdtkg");
u[2] = new User("0001","King","t;stmdtkg");
u[3] = new User("0001","King","t;stmdtkg");
return u;
}
与@ResponseBody对应,我们也可以使用@RequestBody。
比如client端请求时以JSON格式传递参数,我在Controller方法的某个参数前加上@ResponseBody User user则转换为User类型。
另外,注册一个MessageConverter的方式非常简单。
<mvc:annotation-driven>
<mvc:message-converters>
<bean class="pac.king.common.MessageConverter" />
</mvc:message-converters>
</mvc:annotation-driven>本文出自 “Map.get(X)=new Object()” 博客,请务必保留此出处http://runtime.blog.51cto.com/7711135/1412690