当前位置: 代码迷 >> HTML/CSS >> SpringMVC中内容协商【一致资源多种展现json xml html】
  详细解决方案

SpringMVC中内容协商【一致资源多种展现json xml html】

热度:344   发布时间:2013-09-18 14:17:40.0
SpringMVC中内容协商【统一资源多种展现json xml html】

????? 在我们实际的开发中,往往需要SpringMVC服务提供多种的格式的数据。如:JSON、XML、HTML

当然我们知道SpringMVC已经提供了很多种转换器,供我们使用将数据转换成我们想要的数据格式。但是服务者怎么知道使用者,想要使用哪种数据格式哪?这就使用到了SpringMVC中的内容协商。

既然是内容协商,那么使用者肯定会告诉服务者,你给我返回什么类型的数据。使用者可以通过如下方式通知服务者。

一、内容协商

??? 1、使用参数

????

/userController/getUser?format=json
/userController/getUser?format=xml

??? 2、使用扩展名

?

???

/userController/getUser.html
/userController/getUser.json
/userController/getUser.xml

??? 3、使用http的Request Headers中的Accpet

?

???

GET /userController/getUser HTTP/1.1
Accept: application/xml                 //将返回xml格式数据

GET /userController/getUser HTTP/1.1
Accept: application/json               //将返回json格式数据

?以上三种方式分析:

?

?? 1、使用参数

???? 现在很多open API是使用这种方式,但可能由于要编写的字符较多。

??? 淘宝的开放平台就是使用的此种方式



?

2、使用扩展名称

?? 丧失了同一url多种展现的方式,但现在这种在实际环境中是使用比较方便的。

3、使用Request Header中Accpet

?? 这种方式是最理想的,但如果你的资源要给用户直接通过浏览器访问(即html展现),那么由于浏览器的差异,发送上来的Accept Header头将是不一样的.?将导致服务器不知要返回什么格式的数据给你. 下面是浏览器的Accept Header:

Chrome:



?

Firefox:



?

IE9:



?我们使用第一种方式和第二种方式,在Spring中如何让配置

?

二、SpringMVC配置

?? 现spring完成内容协商(content negotiation)的工作是由ContentNegotiatingViewResolver来完成的。

它的工作模式支持我上面讲的三种,ContentNegotiatingViewResolver是根据客户提交的MimeType(如 text/html,application/xml)来跟服务端的一组viewResover的MimeType相比较,如果符合,即返回viewResover的数据。
而 /userController/getUser.xml, ContentNegotiatingViewResolver会首先将 .xml 根据mediaTypes属性将其转换成 application/xml,然后完成前面所说的比较。

?

1、我们看下ContentNegotiatingViewResolver的配置:

<!-- 根据客户端的不同的请求决定不同的view进行响应, 如 /user/getUser.json /user/getUser.xml -->
	<bean
		class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver">
		<!-- 设置为true以忽略对Accept Header的支持 -->
		<property name="ignoreAcceptHeader" value="true" />
		<!-- 在没有扩展名时即: "/user/getUser" 时的默认展现形式 -->
		<property name="defaultContentType" value="text/html" />

		<!-- 扩展名至mimeType的映射,即 /getUser.json => application/json -->
		<property name="mediaTypes">
			<map>
				<entry key="json" value="application/json" />
				<entry key="xml" value="application/xml" />
			</map>
		</property>
		<!-- 用于开启 /user/getUser?format=json 的支持 -->
		<property name="favorParameter" value="false" />
		<property name="viewResolvers">
			<list>
				<bean class="org.springframework.web.servlet.view.BeanNameViewResolver" />
				<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
					<property name="viewClass"
						value="org.springframework.web.servlet.view.JstlView" />
					<property name="prefix" value="/WEB-INF/page/" />
					<property name="suffix" value=".jsp"></property>
				</bean>
			</list>
		</property>
		<property name="defaultViews">
			<list>
				<!-- for application/json -->
				<bean class="org.springframework.web.servlet.view.json.MappingJacksonJsonView" />
				<!-- for application/xml -->
				<bean class="org.springframework.web.servlet.view.xml.MarshallingView">
					<property name="marshaller">
						<!-- xstream.XStreamMarshaller -->
						<bean class="org.springframework.oxm.jaxb.Jaxb2Marshaller">
							<property name="classesToBeBound" >
								<list>
									<value>com.wy.pojo.User</value>
								</list>
							</property>
						</bean> 
					</property>
				</bean>
			</list>
		</property>
	</bean>

?我们在创建User对象

?

@XmlRootElement(name = "User")
public class User implements Serializable {

	private static final long serialVersionUID = 1L;
	private String username;
	private String password;
	private int age;
	private String sex;
	private String birthday;

	@XmlElement
	public String getUsername() {
		return username;
	}

	public void setUsername(String username) {
		this.username = username;
	}

	@XmlElement
	public String getPassword() {
		return password;
	}

	public void setPassword(String password) {
		this.password = password;
	}

	@XmlElement
	public int getAge() {
		return age;
	}

	public void setAge(int age) {
		this.age = age;
	}

	@XmlElement
	public String getSex() {
		return sex;
	}

	public void setSex(String sex) {
		this.sex = sex;
	}

	@XmlElement
	public String getBirthday() {
		return birthday;
	}

	public void setBirthday(String birthday) {
		this.birthday = birthday;
	}

	@Override
	public String toString() {
		// TODO Auto-generated method stub
		return this.username + "#" + this.password + "#" + this.age + "#"
				+ this.sex + "#" + this.birthday;
	}

}

?我们的控制层很简单的

?

@Controller
@RequestMapping("/userController")
public class UserController {
	@RequestMapping(value="getUser")
	public User getUser(){
		User user = new User();
		user.setUsername("wy");
		user.setPassword("123");
		user.setAge(123);
		user.setSex("male");
		user.setBirthday("2013-09-10");
		
		return user;
	}
}

?好,我们看下效果会是怎样子:

?

当我们在浏览器中输入:http://localhost:8080/SpringMVC/userController/getUser.json

结果:

{"user":{"username":"wy","password":"123","age":123,"sex":"male","birthday":"2013-09-10"}}

当我们在浏览器中输入:http://localhost:8080/SpringMVC/userController/getUser.xml

结果:

?

<User><age>123</age><birthday>2013-09-10</birthday><password>123</password><sex>male</sex><username>wy</username></User>

?

将配置文件中的

<!-- 用于开启 /user/getUser?format=json 的支持 -->
		<property name="favorParameter" value="false" />

?中false修改为true

?

我们再次在浏览器中输入:http://localhost:8080/SpringMVC/userController/getUser?format=json

?结果是:

{"user":{"username":"wy","password":"123","age":123,"sex":"male","birthday":"2013-09-10"}}

?

我们再次在浏览器中输入:http://localhost:8080/SpringMVC/userController/getUser?format=xml

结果是:

<User><age>123</age><birthday>2013-09-10</birthday><password>123</password><sex>male</sex><username>wy</username></User>

?

发现两次的请求结果是等效的。

?

下面是ContentNegotiatingViewResolver的完全配置:

<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:context="http://www.springframework.org/schema/context"
        xmlns:mvc="http://www.springframework.org/schema/mvc"
        xmlns:util="http://www.springframework.org/schema/util" 
        xsi:schemaLocation="http://www.springframework.org/schema/beans
          http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
          http://www.springframework.org/schema/context
          http://www.springframework.org/schema/context/spring-context-3.0.xsd
          http://www.springframework.org/schema/mvc    
          http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd
          http://www.springframework.org/schema/util 
          http://www.springframework.org/schema/util/spring-util-3.0.xsd" >
          
    <!-- 默认的注解映射的支持 ,它会自动注册DefaultAnnotationHandlerMapping 与AnnotationMethodHandlerAdapter--> 
    <mvc:annotation-driven />
    
    <!-- 自动扫描注解的Controller -->
	<context:component-scan base-package="com.wy.controller" />
	
	<!-- 根据客户端的不同的请求决定不同的view进行响应, 如 /user/getUser.json /user/getUser.xml -->
	<bean
		class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver">
		<!-- 设置为true以忽略对Accept Header的支持 -->
		<property name="ignoreAcceptHeader" value="true" />
		<!-- 在没有扩展名时即: "/user/getUser" 时的默认展现形式 -->
		<property name="defaultContentType" value="text/html" />

		<!-- 扩展名至mimeType的映射,即 /getUser.json => application/json -->
		<property name="mediaTypes">
			<map>
				<entry key="json" value="application/json" />
				<entry key="xml" value="application/xml" />
			</map>
		</property>
		<!-- 用于开启 /user/getUser?format=json 的支持 -->
		<property name="favorParameter" value="true" />
		<property name="viewResolvers">
			<list>
				<bean class="org.springframework.web.servlet.view.BeanNameViewResolver" />
				<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
					<property name="viewClass"
						value="org.springframework.web.servlet.view.JstlView" />
					<property name="prefix" value="/WEB-INF/page/" />
					<property name="suffix" value=".jsp"></property>
				</bean>
			</list>
		</property>
		<property name="defaultViews">
			<list>
				<!-- for application/json -->
				<bean class="org.springframework.web.servlet.view.json.MappingJacksonJsonView" />
				<!-- for application/xml -->
				<bean class="org.springframework.web.servlet.view.xml.MarshallingView">
					<property name="marshaller">
						<!-- xstream.XStreamMarshaller -->
						<bean class="org.springframework.oxm.jaxb.Jaxb2Marshaller">
							<property name="classesToBeBound" >
								<list>
									<value>com.wy.pojo.User</value>
								</list>
							</property>
						</bean> 
					</property>
				</bean>
			</list>
		</property>
	</bean>

</beans>

?

2、重载MappingJacksonJsonView

? 我们在返回JSON格式的数据时,发现并不是我们常见的json。?也就是我们期望的返回是{success:true,message:”return ok”};? 但实际返回的却是 {"jsonResult":{"success":true,"msg":"return ok"}}
原因是MappingJacksonJsonView中对返回值的处理未考虑modelMap中只有一个值的情况,?直接是按照mapName:{mapResult}的格式来返回数据的。

public class CustomJsonView extends MappingJacksonJsonView {
	protected Object filterModel(Map<String, Object> model) {  
        Map<?, ?> result = (Map<?, ?>) super.filterModel(model);  
        if (result.size() == 1) {  
            return result.values().iterator().next();  
        } else {  
            return result;  
        }  
    }  
}

?3、我们将

<bean class="org.springframework.web.servlet.view.json.MappingJacksonJsonView" />

?替换为我们刚修改的

<bean class="com.wy.view.CustomJsonView"/>

?再次浏览器中输入:http://localhost:8080/SpringMVC/userController/getUser.json

看到的结果这是我们期望的:

{"username":"wy","password":"123","age":123,"sex":"male","birthday":"2013-09-10"}

?

?参考:http://badqiu.iteye.com/blog/552806

  相关解决方案