使用Spring MVC的内容协商

参考: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')
}

2.DispatcherServletInitializer

/**
 * 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[]{"/"};
    }
}

3.WebMvcConfigurerAdapter

/**
 * 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;
    }
}

5.controller

/**
 * 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";
    }
}


6.在tomcat8-rc5部署测试:

三种方式都不到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

你可能感兴趣的:(java)