日期:2014-05-17  浏览次数:20689 次

ContentNegotiatingViewResolver spring REST中的内容协商(同一资源,多种展现:xml,json,html)
本文参照badqiu的文章   http://badqiu.iteye.com/blog/552806

REST的详细可以查看我的一片REST的文章

RESTful服务中很重要的一个特性即是同一资源,多种表述.如 get put post delete head方式提交的请求,或者根据accept,参数,后缀等方式。

get put post delete head方式不介绍。

Accept方式
chrome: 
Accept:application/xml,application/xhtml+xml,textml;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5 

firefox: 
Accept:text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 

IE8: 
Accept:image/gif, image/jpeg, image/pjpeg, image/pjpeg, application/x-shockwave-flash, application/x-silverlight, application/x-ms-application, application/x-ms-xbap, application/vnd.ms-xpsdocument, application/xaml+xml, */*

根据请求的accpet,来返回信息。
由于浏览器的差异,发送上来的Accept Header头将是不一样的. 将导致服务器不知要返回什么格式的数据给你.

StringHttpMessageConverter一文说到的@ResponseBody String返回类型,google chrome下会有问题就是这个原因。

firefox IE6-7由于第一个accept是text/html,StringHttpMessageConverter处理的还是text/html。

使用扩展名
/user/123.xml  将返回xml格式数据 
/user/123.json 将返回json格式数据 
/user/123.html 将返回html格式数据 

丧失了同一url多种展现的方式。在rest架构中,user/123应该具有不同的展示。而/user/123.xml和/user/123.json则已经是不同的url了。

使用参数
  现在很多open API是使用这种方式,但可能由于要编写的字符较多(占用更多带宽),所以较少使用.

ContentNegotiatingViewResolver
注意这个ContentNegotiatingViewResolver视图解析器只有在handler方法, return(controller的返回值,经过处理后,最后会成为ModelAndView)值,如果return值只是一个普通的bean,那么处理后的ModelAndView的内容如下图





不为null才会处理。而对于在方法前面写了ResponseBody注解的方法,
AnnotationMethodHandlerAdapter类会判断
else if (AnnotationUtils.findAnnotation(handlerMethod, ResponseBody.class) != null) {
handleResponseBody(returnValue, webRequest);
return null;
}
,如上,当有ResponseBody注解,会调用handleResponseBody,并把return null设置为空。


这个类它实现了ViewResolver。但它并不直接解析视图,而是委托给别人。默认情况,它是从spring 上下文,查找视图解析器,并调用这些解析器。也可以在初始化这个bean的时候,设置它的解析器属性(viewResolvers),这是个list类型的属性。

请注意,要让这个视图解析器正常工作,需要设置比别人更高的优先级(默认为Ordered.HIGHEST_PRECEDENCE)。

配置的例子
<!--
根据客户端的不同的请求决定不同的view进行响应, 如 /rest/1.json /rest/1.xml /rest?format=json
/rest?format=xml
-->
<bean
class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver">
<!-- 设置为true以忽略对Accept Header的支持-->
<property name="ignoreAcceptHeader" value="true" />
<!-- true,开启扩展名支持,false关闭支持 -->
<property name="favorPathExtension" value="false" />
<!-- 用于开启 /userinfo/123?format=json的支持 -->
<property name="favorParameter" value="true" />
<!--
在没有扩展名和参数时即: "/user/1" 时的默认展现形式
-->
<property name="defaultContentType" value="text/html" />

<!--
参数值至mimeType的映射,即 /rest?format=json json是key,application/json就是value
暂时只支持json和xml
-->
<property name="mediaTypes">
<map>
<entry key="json" value="application/json" />
<entry key="xml" value="application/xml" />
</map>
</property>

<property name="viewResolvers">
<!-- 关闭所有的解析器,防止它在查找候选视图时多个解析器都运行 -->
<list></list>
</property>

<property name="defaultViews">
<list>
<!-- for application/json -->
<bean
class="org.springframework.web.servlet.view.json.MappingJacksonJsonView">
<!-- <property name="renderedAttributes">-->
<!-- <set>-->
<!-- <value>result</value>-->
<!-- <value>user</value>-->
<!-- <value>p