参考: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<HttpMessageConverter<?>> 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.主要依赖
<dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.6.4</version> </dependency> <dependency> <groupId>org.codehaus.woodstox</groupId> <artifactId>woodstox-core-asl</artifactId> <version>4.4.1</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.dataformat</groupId> <artifactId>jackson-dataformat-xml</artifactId> <version>2.6.4</version> </dependency> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi</artifactId> <version>3.10-FINAL</version> </dependency>
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<String, Object> model, Workbook workbook, HttpServletRequest request, HttpServletResponse response) throws Exception { String servletPath = request.getServletPath(); /* /user/list.xls */ if (servletPath.startsWith("/user/list")){//根据不同的请求(这里根据servletPath判断),生成不同的excel List<User> users = (List<User>) 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<User> 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