首页 > 代码库 > 【SpringMVC】根据请求处理资源表述

【SpringMVC】根据请求处理资源表述

通常一个web应用中,资源都在浏览器中以HTML的形式表述。
但对于一个RESTful的资源应以多种形式表述,用户要求什么就以什么样的形式表述。
比如HTML、XML、JSON、PDF、EXCEL...


Spring有两种方式处理表述形式:
·org.springframework.web.servlet.view.ContentNegotiatingViewResolver
·org.springframework.http.converter.HttpMessageConverter


一、ContentNegotiatingViewResolver

虽然也是ViewResolver一族的,但他并不解析视图,而是解析视图的任务委托给其他ViewResolver。
默认情况下会从所有ViewResolver中自动选择合适的ViewResolver,但为了更明确的表述,我们会选择使用setViewResolvers(List<ViewResolver> viewResolvers)更明确地定义ViewResolver。
ContentNegotiatingViewResolver委托所有viewResolvers解析视图后将视图存放在待选视图列表(candidateViews)中,如果设置了defaultView,默认视图会在列表的尾部。
wKioL1N23umBDqb1AAI57aMF_cg601.jpg


如何确定请求的媒体类型? 虽然accept header里也可以获得请求的表述形式,但ContentNegotiatingViewResolver会先考虑URL后面的扩展名。
从扩展名无法得到类型时将考虑accept header。如果此时仍然无法获得有效的类型,将使用defaultContentType设置(先已在ContentNegotiatingViewResolver中deprecated,可以在org.springframework.web.accept.ContentNegotiationManagerFactoryBean中看到)。
我们可以自定义扩展名和媒体类型映射。
看一些书和一些博客直接使用了ContentNegotiatingViewResolver的setMediaTypes(Map<String, String> mediaTypes),但值得注意的是,这个方法已经deprecated了(当然,与之有关的一系列setFavorPathExtension、setFavorParameter、setIgnoreAcceptHeader什么的都deprecated了,但他们都移到了ContentNegotiationManager中)。
现在建议使用setContentNegotiationManager(ContentNegotiationManager contentNegotiationManager),但并不复杂,只是变得更加OO而已。
另外org.springframework.web.accept.ContentNegotiationManagerFactoryBean是ContentNegotiationManager的:
A factory providing convenient access to a {@code ContentNegotiationManager} configured with one or more {@link ContentNegotiationStrategy} instances.


配置contentNegotiationMananger:

<bean id="contentNegotiationManager"
    class="org.springframework.web.accept.ContentNegotiationManagerFactoryBean">
    <property name="favorParameter" value="http://www.mamicode.com/true" />
    <property name="parameterName" value="http://www.mamicode.com/format" />
    <property name="ignoreAcceptHeader" value="http://www.mamicode.com/false" />
    <property name="mediaTypes">
        <value>
            json=application/json
            xml=application/xml
        </value>
    </property>
    <property name="defaultContentType" value="http://www.mamicode.com/text/html" />
</bean>

话说favorPathExtension默认就是true,因此在这里就不做设置了。
favorPathExtension为false则勿略扩展名。
favorParameter与parameterName是一对属性。
favorParameter设置为true可以用[?format=表述形式]这种方式。
默认值是format,这个值可以通过parameterName来设置。
ignoreAcceptHeader默认为false,即不忽略Accept信息。


配置ContentNegotiatingViewResolver:

    <bean class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver">
        <property name= "contentNegotiationManager" ref= "contentNegotiationManager"/>
<!--         <property name="mediaTypes"> -->
<!--             <map> -->
<!--                 <entry key="atom" value="http://www.mamicode.com/application/atom+xml" /> -->
<!--                 <entry key="html" value="http://www.mamicode.com/text/html" /> -->
<!--                 <entry key="json" value="http://www.mamicode.com/application/json" /> -->
<!--             </map> -->
<!--         </property> -->
        <property name="viewResolvers">
            <list>
<!--                 <bean class="org.springframework.web.servlet.view.BeanNameViewResolver" /> -->
                <bean
                    class="org.springframework.web.servlet.view.InternalResourceViewResolver">
                    <property name="prefix" value="http://www.mamicode.com/WEB-INF/views/" />
                    <property name="suffix" value="http://www.mamicode.com/.jsp" />
                </bean>
            </list>
        </property>
        <property name="defaultViews">
            <list>
                <bean class="org.springframework.web.servlet.view.json.MappingJacksonJsonView" />
            </list>
        </property>
    </bean>

ContentNegotiatingViewResolver的setMediaTypes没deprecated时是想上面那样写的,现在都写到contentNegotiationMananger了。


二、HttpMessageConverter

通常可以看到把对象放到Model中在View中渲染。使用HttpMessageConverter则可以将方法返回的对象转为要求的表述形式。
wKiom1N24TTR6e4VAAInCzf7a-g348.jpg


上面是实现类,很多都是自动注册的。
org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport

protected final void addDefaultHttpMessageConverters(List<HttpMessageConverter<?>> messageConverters) {
    StringHttpMessageConverter stringConverter = new StringHttpMessageConverter();
    stringConverter.setWriteAcceptCharset(false);
    messageConverters.add(new ByteArrayHttpMessageConverter());
    messageConverters.add(stringConverter);
    messageConverters.add(new ResourceHttpMessageConverter());
    messageConverters.add(new SourceHttpMessageConverter<Source>());
    messageConverters.add(new AllEncompassingFormHttpMessageConverter());
    if (romePresent) {
        messageConverters.add(new AtomFeedHttpMessageConverter());
        messageConverters.add(new RssChannelHttpMessageConverter());
    }
    if (jaxb2Present) {
        messageConverters.add(new Jaxb2RootElementHttpMessageConverter());
    }
    if (jackson2Present) {
        messageConverters.add(new MappingJackson2HttpMessageConverter());
    }
    else if (jacksonPresent) {
        messageConverters.add(new org.springframework.http.converter.json.MappingJacksonHttpMessageConverter());
    }
}


另外我们需要用到@ResponseBody,以表示Controller的返回值会绑定在响应体中,并将其转换为客户端请求的表述形式。
请求的表述形式主要是Accept头信息,如果请求中不包括Accept头信息则会假设可以接受任何表述形式。当然,如果在方法的@RequestMapping中设置了Accept头信息则只会处理包含这些Accept信息的请求。

比如下面的代码中我要将User数组转为JSON形式表述:

@RequestMapping(value ="http://www.mamicode.com/userList",headers={"Accept="+MediaType.APPLICATION_JSON_VALUE})
public @ResponseBody User[] queryUserList(){
    User[] u = new User[4];
    u[0] = new User("0001","King","t;stmdtkg");
    u[1] = new User("0001","King","t;stmdtkg");
    u[2] = new User("0001","King","t;stmdtkg");
    u[3] = new User("0001","King","t;stmdtkg");
    return u;
}


@ResponseBody对应,我们也可以使用@RequestBody
比如client端请求时以JSON格式传递参数,我在Controller方法的某个参数前加上@ResponseBody User user则转换为User类型。


另外,注册一个MessageConverter的方式非常简单。

<mvc:annotation-driven>
    <mvc:message-converters>
        <bean class="pac.king.common.MessageConverter" />
    </mvc:message-converters>
</mvc:annotation-driven>



本文出自 “Map.get(X)=new Object()” 博客,请务必保留此出处http://runtime.blog.51cto.com/7711135/1412690