`
liyixing1
  • 浏览: 958191 次
  • 性别: Icon_minigender_1
  • 来自: 江西上饶
社区版块
存档分类
最新评论

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>page</value>-->
					<!--						</set>-->
					<!--					</property>-->
					<!-- 重新设置 objectMapper-->
					<property name="objectMapper">
						<bean class="org.codehaus.jackson.map.ObjectMapper">
							<!--
								设置objectMapper的serializationConfig的serializationInclusion属性,以忽略null对象
							-->
							<property name="serializationConfig.serializationInclusion">
								<value
									type="org.codehaus.jackson.map.annotate.JsonSerialize.Inclusion">NON_NULL</value>
							</property>
						</bean>
					</property>
				</bean>
				<!-- for application/xml -->
				<bean class="org.springframework.web.servlet.view.xml.MarshallingView">
					<property name="marshaller">
						<bean class="org.springframework.oxm.castor.CastorMarshaller">
							<property name="validating" value="false"></property>
						</bean>
					</property>
				</bean>
			</list>
		</property>
	</bean>




这个视图解析器根据请求类型来返回视图。就是上面说的三种方式。

1.如果设置了setFavorPathExtension(boolean)为true,会根据后缀来使用不同的数据格式。

2.如果设置了setFavorParameter(boolean)为true,会根据请求参数来设置不同的数据格式。参数名是通过属性parameterName设置的,默认private String parameterName = "format";

3.如果ignoreAcceptHeader未设置为false(未关闭),那么会通过accpet 来获得相应的信息。

在其内部中,是通过

protected List<MediaType> getMediaTypes(HttpServletRequest request) {

方法来根据请求过来的信息,返回对应的需要的回应的context-type的。首先判断的是扩展名是否开启,

if (this.favorPathExtension) {

如果开启,先获取扩展名,如果扩展名不为null,
查看是否有可以处理的扩展,这个是将扩展名作为key,来获取的

MediaType mediaType = this.mediaTypes.get(extension);

这里的mediaTypes就是刚才的配置
<property name="mediaTypes">
			<map>
				<entry key="json" value="application/json" />
				<entry key="xml" value="application/xml" />
			</map>
		</property>


设置的值。
如果我们访问的是/127.0.0.1/daowole/rest.xml
扩展名就是xml,这个key对应的value就是application/xml。

有则返回媒体类型。扩展名得方式还会判断useJaf是否开启。开启的话会使用

jaf(Java Activation Framework)

来获取,也就是通过

String mediaType = fileTypeMap.getContentType(fileName);

来获取媒体类型。这里的fileTypeMap是一个静态常量。它里面建立了一张还算完整的根据文件名字的后缀,映射的媒体表格。

如(其中的一部分)
c++=MIMETypeEntry: text/plain,
bcpio=MIMETypeEntry: application/x-bcpio,
xwd=MIMETypeEntry: image/x-xwindowdump,

如果是从jaf取出来的,会自动往mediaTypes添加内容。

if (mediaType != null) {
this.mediaTypes.putIfAbsent(extension, mediaType);
}


否则进入下一个处理方式,
下一个处理方式是参数,判断favorParameter是否开启。处理类似
通过参数。只是将参数值,作为key从mediaTypes获取。

然后然后是accept方式,判断ignoreAcceptHeader是否开启。处理类似,但不需要从mediaTypes获取信息。

如果没有,查看defaultContentType是否为空,返回的是defaultContentType

否则返回return Collections.emptyList();一个空的内容。


其次根据返回的
List<MediaType> requestedMediaTypes,controller返回的viewname,以及locale,来生成多个候选的View(List<View>)
List<View> candidateViews = getCandidateViews(viewName, locale, requestedMediaTypes);

/**发现候选的方式是遍历它的viewResolvers属性(这个类在构造的时候,会从spring上下文把所有的解析器取出来,当然我们也可以通过bean初始化的property元素设置它的viewResolvers属性),将每个viewResolver拿出来,调用它的resolveViewName,来获取view,如果view有值,表示这个视图解析器可以处理(可以处理不表示只有这一个是候选的,候选可能是多个的)。这个时候还没结束*/

for (ViewResolver viewResolver : this.viewResolvers) {
			View view = viewResolver.resolveViewName(viewName, locale);
			if (view != null) {
//不为null,添加到候选视图
				candidateViews.add(view);
			}

//这里还需要遍历需要返回给客户端的媒体类型
			for (MediaType requestedMediaType : requestedMediaTypes) {
//根据媒体类型,返回扩展名,List<String> extensions = getExtensionsForMediaType(requestedMediaType);这部做的是和通过key获取value方式反了一下,而是通过value获取key了,也就是通过媒体,获取这个媒体在mediaType中的key,这里返回的key可能是多个,也就是说一种媒体可以对应各种文件格式,如json可以对应application/json text/json。

				List<String> extensions = getExtensionsForMediaType(requestedMediaType);

//遍历这个媒体,通过String viewNameWithExtension = viewName + "." + extension;操作后,生成新的逻辑视图,并查看这个逻辑是否也可以在当前的视图解析器处理出一个物理视图。如我们的action是/daowole/test.json 本来我们controller返回的逻辑视图是 order/addsuccess,它对应的物理视图是 order/addsuccess.ftl,而这个时候会增加逻辑视图, order/addsuccess.json 那么对应的物理视图就是 order/addsuccess.json.ftl
				for (String extension : extensions) {
					String viewNameWithExtension = viewName + "." + extension;
					view = viewResolver.resolveViewName(viewNameWithExtension, locale);
//可以处理,将它也加入到候选视图。
					if (view != null) {
						candidateViews.add(view);
					}
				}

			}
		}


if (!CollectionUtils.isEmpty(this.defaultViews)) {
//这里会把我们在上面设置的默认视图(视图不是视图解析器)也加入进去。我们这里设置的默认视图只有一个json
			candidateViews.addAll(this.defaultViews);
		}
当然这里的视图比较特殊,它不需要逻辑视图名,就可以直接返回responsebody了。

随后需要从候选视图查找一个唯一可以处理的视图,也就是
View bestView = getBestView(candidateViews, requestedMediaTypes);

代码如下
MediaType bestRequestedMediaType = null;
		View bestView = null;
//首先是遍历需要回应给客户端的媒体信息
		for (MediaType requestedMediaType : requestedMediaTypes) {
//这里是遍历每个候选视图
			for (View candidateView : candidateViews) {
				if (StringUtils.hasText(candidateView.getContentType())) {
//获取当前候选时候的媒体信息
					MediaType candidateContentType = MediaType.parseMediaType(candidateView.getContentType());
//判断这个候选视图媒体是否在位于需要回应的媒体中,至于怎么判断是否位于这个媒体中我们不做详细介绍,但候选视图的媒体刚好包含于回应视图中的,那么
					if (requestedMediaType.includes(candidateContentType)) {
						bestRequestedMediaType = requestedMediaType;
//baseView就确定了。
						bestView = candidateView;
						break;
					}
				}
			}
			if (bestView != null) {
				if (logger.isDebugEnabled()) {
					logger.debug(
							"Returning [" + bestView + "] based on requested media type '" + bestRequestedMediaType +
									"'");
				}
				break;
			}
		}
		return bestView;

这里返回后,也就是
View bestView = getBestView(candidateViews, requestedMediaTypes);执行后

if (bestView != null) {
			return bestView;
		}

else {
//这里生成是一个404
			if (this.useNotAcceptableStatusCode) {
				if (logger.isDebugEnabled()) {
					logger.debug("No acceptable view found; returning 406 (Not Acceptable) status code");
				}
				return NOT_ACCEPTABLE_VIEW;
			}
			else {
				if (logger.isDebugEnabled()) {
					logger.debug("No acceptable view found; returning null");
				}
//任何都不返回,这个useNotAcceptableStatusCode设置的时候要注意了,如果为true,而且没有找到任何可以处理的view,那么就会404,否则,它就return null。当spring mvc 的servlet接受到的view为null,那么就会交给下一个视图解析器处理,那么原来的解析器就依然可以起到效果了。这种情况是我们大多数应用所希望的。所以spring mvc在设计这个类得时候把它的默认值设置为false。
				return null;
			}
		}
  • 大小: 26.2 KB
分享到:
评论
9 楼 style2013 2014-12-09  
liyixing1 写道
style2013 写道
顶顶  求楼主联系方式。。

联系方式?这是要约吗,你性别属性是啥呢


北京约啦。。
8 楼 liyixing1 2014-12-09  
style2013 写道
顶顶  求楼主联系方式。。

联系方式?这是要约吗,你性别属性是啥呢
7 楼 style2013 2014-12-09  
顶顶  求楼主联系方式。。
6 楼 liyixing1 2014-11-14  
cpu0oop 写道
确实是好文章

多谢
5 楼 cpu0oop 2014-11-14  
确实是好文章
4 楼 liyixing1 2012-05-16  
wenxiang_tune 写道
必须要顶,这文章写得好,希望以后能多沟通

呵呵,谢谢夸奖
3 楼 wenxiang_tune 2012-05-15  
必须要顶,这文章写得好,希望以后能多沟通
2 楼 liyixing1 2012-04-26  
liheping17 写道
这么好的文章 怎么没人顶

呵呵,多谢多谢
1 楼 liheping17 2012-04-25  
这么好的文章 怎么没人顶

相关推荐

    Spring @MVC REST 实例以及 (同一资源,多种展现:xml,json,html)

    该实例展示了 spring3 REST 的使用方法,以及同一种内容,多种不同方式展示的实现,例如同一 User 信息,可以展示为:xml,json,html三种不同的格式。可以参考这篇文章:http://badqiu.javaeye.com/blog/552806

    eclipse + maven多模块项目 + SpringMVC + jetty热部署实现验证码图片实例源码

    并且基于Spring MVC提供了一个完整功能:实现了生成验证码图片,以及验证输入是否匹配的两个接口,接口为Rest风格,符合内容协商原则(同一资源,多种展现:xml,json,html)。 另外,演示了注解(Annotation)的用法,实现...

    CXF2.6 spring 提供rest服务输出xml或json等格式数据

    在IT行业中,构建RESTful服务...总结一下,通过CXF 2.6与Spring的集成,我们可以轻松地创建REST服务并支持多种数据格式的输出,如XML和JSON。这使得我们的应用程序更具有灵活性和互操作性,符合现代Web服务的设计原则。

    Apache CXF + Spring3 + REST + JSON配置

    在"Apache CXF + Spring3 + REST + JSON配置"中,我们主要探讨如何利用Apache CXF和Spring 3框架来构建RESTful服务,并使用JSON作为数据交换格式。以下是一些关键知识点: 1. **Spring 3集成CXF**: - 首先,你...

    springmvc+velocity+ Rest Services(xml,json)实例

    **Spring MVC + Velocity + REST Services (XML, JSON) 实例详解** 在当今的Web开发领域,Spring MVC、Velocity和RESTful服务是常见的技术栈。这个实例项目是一个基于Maven构建的无数据库操作的轻量级Web应用,它...

    Building a Rest API with Spring

    本文主要介绍了使用Spring框架构建REST API的全过程,内容包括了从基础的Spring Web应用搭建,到使用Java配置,再到通过Spring Security进行安全认证和授权,以及REST API的其他重要特性,例如:使用HTTP消息转换器...

    spring mvc rest基础学习demo

    Spring MVC 是一个强大的Java Web应用程序框架,用于构建高效、可维护的Web应用。它扩展了Spring框架的功能,提供了模型-视图-...在实际操作中,读者可以按照文件名"spring_mvc"中的内容逐步实践每个步骤,加深理解。

    Rest输出Json的WebService项目实例

    本项目实例“Rest输出Json的WebService项目”便是一个展示如何利用RESTful接口发送和接收JSON数据的实例,其中整合了MyBatis和Spring两大框架,提供了一个完整的解决方案。 1. RESTful API设计:REST的核心思想是...

    csh框架+cxf+spring+hibernate+mysql 注解 annotation xml json

    【标题】"csh框架+cxf+spring+hibernate+mysql 注解 annotation xml json" 提到的是一个基于Java技术栈的Web服务开发架构,涵盖了多个关键组件和技术,这些技术在现代企业级应用中广泛应用。以下是这些知识点的详细...

    使用Spring MVC创建REST服务简单例子

    在`servlet-context.xml`中,我们将配置Spring MVC的处理器映射器、视图解析器和其他相关组件: ```xml &lt;beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi=...

    rest+spring+hibernate

    "rest+spring+hibernate"这个主题结合了这三个关键技术,主要探讨如何在Spring框架中实现RESTful API,并利用Hibernate进行数据访问。 1. **RESTful API设计**:REST原则强调资源的表述和状态转移,通过HTTP动词...

    spring rest mvc使用RestTemplate调用

    `RestTemplate`支持多种数据格式的转换,如JSON、XML。默认情况下,它包含`MappingJackson2HttpMessageConverter`来处理JSON。如果需要其他格式,可以添加更多的转换器。 6. **自定义请求头** 可以使用`...

    SpringMVC构建REST接口:第四篇 第一个REST风格的接口的源代码

    在本篇中,我们将深入探讨如何使用Spring MVC构建RESTful接口,主要关注的是在实际项目中创建第一个REST风格接口的源代码实现。REST(Representational State Transfer)是一种网络应用程序的设计风格和开发方式,...

    使用Spring4 MVC 构建RESTful服务器,输出JSON格式的数据结构

    Spring4 MVC作为Java领域最流行的MVC框架之一,提供了一流的支持来构建RESTful API,尤其适合输出JSON格式的数据结构。本文将深入探讨如何使用Spring4 MVC实现这一目标。 首先,理解REST(Representational State ...

    springmvc+rest+json交互+接口

    接下来,我们来看看如何在Spring MVC中实现RESTful接口并使用JSON: 1. **配置Spring MVC**:首先,你需要在Spring的配置文件中启用MVC支持,添加`&lt;mvc:annotation-driven&gt;`标签。这将自动配置必要的处理器,支持...

    Rest简介及Spring实现

    了解REST和Spring中的REST服务实现是现代Web开发的重要组成部分。通过REST,我们可以构建高效、可扩展的API,方便客户端进行数据交互。Spring框架提供了强大的工具,简化了REST服务的开发过程,使开发者能够快速、...

    rest webservice demo spring

    3. **资源表示**: 在RESTful设计中,每个URL被视为一个资源,资源的表示通常由JSON或XML提供。在Spring中,可以使用`@ResponseBody`注解将方法返回的对象转换为JSON格式发送到客户端。 4. **HTTP方法**: RESTful ...

    Spring MVC – Easy REST-Based JSON Services with @ResponseBody

    标题中的“Spring MVC – Easy REST-Based JSON Services with @ResponseBody”是指使用Spring MVC框架构建基于REST的JSON服务,并通过使用`@ResponseBody`注解来简化这一过程。REST(Representational State ...

    第四章 Spring MVC Rest风格的url、静态资源标签

    在本章中,我们将深入探讨Spring MVC框架中的RESTful风格URL设计以及如何处理静态资源。REST(Representational State Transfer)是一种软件架构风格,常用于Web服务设计,它强调通过HTTP方法(如GET、POST、PUT、...

    jersey+spring rest webservice web maven

    标题中的"jersey+spring rest webservice web maven"是一个基于Java开发的RESTful Web服务架构,它结合了几个关键的技术组件。让我们深入探讨每个部分: 1. **Jersey**: Jersey是Java RESTful Web服务(JAX-RS)...

Global site tag (gtag.js) - Google Analytics