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