????? 在我们实际的开发中,往往需要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