参考:http://spring.io/blog/2013/05/11/content-negotiation-using-spring-mvc
此文主要实践RESTful内容协商,另一种内容协商在ViewResolver动手脚,此文不去实践了:http://spring.io/blog/2013/06/03/content-negotiation-using-views
spring支持三种方式来达到请求所需的content-type:url后缀,url参数,请求接收头.检测顺序:url后缀->url参数->请求接收头.经过三步都不能确认content-type,毫无疑问是设置中的default.
开始吧.....
1.build.gradle
apply plugin:'war'
targetCompatibility = 1.7
version = '1.0'
repositories{
mavenCentral()
}
dependencies{
compile 'org.springframework:spring-webmvc:3.2.5.RELEASE'
compile 'com.fasterxml.jackson.core:jackson-databind:2.3.0'
providedCompile 'org.apache.tomcat:tomcat-servlet-api:8.0.0-RC5'
}
tasks.create(name: 'deploy',dependsOn:'war', type: Copy) {
from(file("$buildDir/libs/gradletest-1.0.war"))
into('/Volumes/3/tomcat8/webapps')
}
/**
* User: xiejx618
* Date: 13-11-25
* Time: 下午2:57
*/
public class DispatcherServletInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
@Override
protected Class>[] getRootConfigClasses() {
return null;
}
@Override
protected Class>[] getServletConfigClasses() {
return new Class>[]{AppConfig.class};
}
@Override
protected String[] getServletMappings() {
return new String[]{"/"};
}
}
/**
* User: xiejx618
* Date: 13-11-25
* Time: 下午1:53
*/
@EnableWebMvc
@Configuration
@ComponentScan(basePackages={"com.test.web"})
public class AppConfig extends WebMvcConfigurerAdapter {
@Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
@Override
public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
configurer.favorPathExtension(true).useJaf(false)
.favorParameter(true).parameterName("mediaType")
.ignoreAcceptHeader(true).
defaultContentType(MediaType.APPLICATION_JSON).
mediaType("xml", MediaType.APPLICATION_XML).
mediaType("json", MediaType.APPLICATION_JSON);
}
/*
@Override
public void configureMessageConverters(List> converters) {
MappingJackson2HttpMessageConverter jmc = new MappingJackson2HttpMessageConverter();
jmc.setPrettyPrint(true);
converters.add(jmc);
Jaxb2RootElementHttpMessageConverter j2 = new Jaxb2RootElementHttpMessageConverter();
converters.add(j2);
}
*/
@Bean
public InternalResourceViewResolver getInternalResourceViewResolver() {
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
resolver.setPrefix("/WEB-INF/");
resolver.setSuffix(".jsp");
return resolver;
}
}
4.pojo person
/**
* User: xiejx618
* Date: 13-11-25
* Time: 下午4:48
*/
@XmlAccessorType(XmlAccessType.PROPERTY)
@XmlRootElement(name = "person")
@XmlType(name="personType")
public class Person {
private String username;
private int age;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
/**
* User: xiejx618
* Date: 13-11-25
* Time: 下午1:45
*/
@Controller
@RequestMapping("/sample")
public class SampleController {
@RequestMapping(value = "/test", produces={MediaType.APPLICATION_JSON_VALUE, MediaType.APPLICATION_XML_VALUE})
@ResponseStatus(HttpStatus.OK)
public @ResponseBody Person test() {
Person person = new Person();
person.setAge(18);
person.setUsername("Aha");
return person;
}
@RequestMapping(value = "/testAsJsp")
public String testAsJsp() {
return "test";
}
}
三种方式都不到mediaType,那只能是默认的json::http://localhost:8080/gradletest-1.0/sample/test
通过后缀json得到mediaType,是json格式:http://localhost:8080/gradletest-1.0/sample/test.json
通过后缀xml得到mediaType,是xml格式:http://localhost:8080/gradletest-1.0/sample/test.xml
通过url参数得到mediaType,是json格式:http://localhost:8080/gradletest-1.0/sample/test?mediaType=json
通过url参数得到mediaType,是xml格式:http://localhost:8080/gradletest-1.0/sample/test?mediaType=jxml
后缀,url参数都有,后缀优先得到mediaType是xml格式:http://localhost:8080/gradletest-1.0/sample/test.xml?mediaType=json
本来的请求头就乱七八糟的,使用请求头去控,还是弱了点....
另参考:http://spring.io/blog/2013/05/11/content-negotiation-using-spring-mvc
要注意的是pojo Person为什么要标注XmlRootElement.参考:http://docs.spring.io/spring/docs/3.2.x/javadoc-api/org/springframework/http/converter/xml/Jaxb2RootElementHttpMessageConverter.html.
其中有说明:This converter can read classes annotated with XmlRootElement
and XmlType
, and write classes annotated with with XmlRootElement
, or subclasses thereof.
源码:下载
到了spring 4.1,内容协商更简单.
1.主要依赖
com.fasterxml.jackson.core
jackson-databind
2.6.4
org.codehaus.woodstox
woodstox-core-asl
4.4.1
com.fasterxml.jackson.dataformat
jackson-dataformat-xml
2.6.4
org.apache.poi
poi
3.10-FINAL
2.Web配置类.
@Override
public void configureViewResolvers(ViewResolverRegistry registry) {
MappingJackson2JsonView jsonView = new MappingJackson2JsonView();
jsonView.setPrettyPrint(true);
MappingJackson2XmlView xmlView = new MappingJackson2XmlView();
xmlView.setPrettyPrint(true);
registry.enableContentNegotiation(jsonView,xmlView,new CustomXlsView());
registry.jsp("/WEB-INF/", ".jsp");
}
@Override
public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
configurer.ignoreAcceptHeader(true).defaultContentType(MediaType.TEXT_HTML);
}
因为spring mvc没提供AbstractXlsView的实现类,就自定义了CustomXlsView.
package org.exam.config;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.exam.domain.User;
import org.springframework.web.servlet.view.document.AbstractXlsView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.List;
import java.util.Map;
/**
* Created by xin on 16.1.7.
*/
public class CustomXlsView extends AbstractXlsView {
@Override
protected void buildExcelDocument(Map model, Workbook workbook, HttpServletRequest request, HttpServletResponse response) throws Exception {
String servletPath = request.getServletPath();
/* /user/list.xls */
if (servletPath.startsWith("/user/list")){//根据不同的请求(这里根据servletPath判断),生成不同的excel
List users = (List) model.get("users");
Sheet sheet = workbook.createSheet("User Statistics");
int rowNum=0;
Row row0 = sheet.createRow(rowNum++);
row0.createCell(0).setCellValue("ID");
row0.createCell(1).setCellValue("Username");
row0.createCell(2).setCellValue("Password");
row0.createCell(3).setCellValue("Height");
for (User user : users) {
Row row = sheet.createRow(rowNum++);
row.createCell(0).setCellValue(user.getId());
row.createCell(1).setCellValue(user.getUsername());
row.createCell(2).setCellValue(user.getPassword());
row.createCell(3).setCellValue(user.getHeight());
}
}
}
}
enableContentNegotiation方法参数是可变的,也可以实现pdf之类的视图.
3.Controller一个方法可以实现三种视图
@RequestMapping(value = "list",method = RequestMethod.GET)
public String list(Model model) {
List list = new ArrayList<>();
for (int i = 0; i < 3; i++) {
User user = new User();
user.setUsername("xie" + i);
user.setPassword("pass" + i);
user.setId((long) i);
list.add(user);
}
model.addAttribute("users", list);
return "user/list";
}
返回jsp视图:list.htm或list.html或list或list.do
返回json数据:list.json
返回xml数据:list.xml
生成xls文件:list.xls
如果不想配置内容协商,想实现导出xls功能,可以参考http://blog.csdn.net/xiejx618/article/details/38906683