转载

内容协商 Spring ContentNegotiation

在SpringMVC中,我们访问一个 RESTful @ReponseBody 接口时,spring可以实现根据path extension来给出不同的响应格式,如:

// Json

curl http://localhost:8080/springautowired/foo.json

{"foo":"bar"}

// Xml

curl http://localhost:8080/springqutowired/foo.xml

<?xml version="1.0" encoding="UTF-8"?>

<root><foo>bar</foo></root>

这是spring内容协商的一种外在表现形式。

内容协商,ContentNegotiation 在spring中关注的核心问题是: 你想要什么样格式的数据 你想要什么,可以理解为, http请求响应时,服务器应该往http response body里写什么格式的数据

分层概念

在SpringMVC的概念中,View指的是数据的展现形式,Model可以理解成领域数据模型或者载体,MVC的核心是分层,分层的目的是为了解耦。就View与Model之间的解耦,指的是数据本身与数据展示形式的解耦,在spring应用内部,领域数据的载体,可以简单理解为bean。到了View层,数据的展现形式多种多样,可以是Jsp, velocity,pdf 或者RESTful的json, xml等。

使用SpringMVC时,通常有两种方式产生输出:

  • 使用RESTful 的 @ ResponseBody,借助于HttpMessageConverter来输出像Json Xml等类型的的数据

  • 使用 view resolution,可以生成更传统的Html页面(Jsp、Velocity等)

无论用哪种方式,你都有可能需要把Controller返回的相同的数据内容转成不同的表现形式。

在之前的内容中有提到HttpMessageConverter是spring处理rest请求时负责解析与转换http消息的逻辑单元。借助HttpMessageConverter,sping可以实现应用领域数据bean与http消息body的转换。 那么问题来了,当前一个http请求完成时,该用哪个具体的HttpMessageConverter实现来回写数据,这就是ContentNegotiation关注的内容。

协商策略

提到协商,必须有协商的点和策略,对应一个http请求,spring来判定请求中htto body格式主要通过以下三个因素(优先级同顺序):

  • path extension  也称为path suffix 就是文章开头提到的例子中的url后缀 .json .xml

  • url parameter 是一个明确指定媒体类型的参数,即通过显式的参数告诉服务器,我想要什么格式的数据。如format=json,  参数名可指定,默认是format

  • http header ( Accept) 如果上边两项都没有,能参考http header Accept来判定响应数据格式,因某些浏览器或者有些http请求不完全按规则来指定需要的媒体类型,所以使用时需要谨慎使用header

上述协商的规则,在spring中被抽象为接口:

// A strategy for resolving the requested media types

// for a request.

ContentNegotiationStrategy

内容协商 Spring ContentNegotiation

  • FixContentNegotiationStrategy一般用来处理默认数据格式

  • HeaderContentNegotiationStrategy用来处理http Accept header的方式

  • ParameterContentNegotiationStrategy用来处理显式的媒体类型参数方式

  • PathExtensionContentNegotiationStrategy处理url后缀方式

上边这些strategy实现类以组合模式的形式封装成ContentNegotiationManager,对外提供逻辑接口。

ContentNegotiationManager的resolveMediaTypes方法如下:

@Override

public List<MediaType> resolveMediaTypes(NativeWebRequest request)

throws HttpMediaTypeNotAcceptableException {

for (ContentNegotiationStrategy strategy : this.strategies) {

List<MediaType> mediaTypes = strategy.resolveMediaTypes(request);

if (mediaTypes.isEmpty() || mediaTypes.equals(MEDIA_TYPE_ALL)) {

continue;

}

return mediaTypes;

}

return Collections.emptyList();

}

使用方式

在项目中,可以通过ContentNegotiationManagerFactoryBean来配置一个全局的内容协商contentNegotiationManager,给spring MVC使用,ContentNegotiationManagerFactoryBean中主要属性如下:

public class ContentNegotiationManagerFactoryBean

implements FactoryBean<ContentNegotiationManager>,

ServletContextAware, InitializingBean {

//是否关注url路径后缀

private boolean favorPathExtension = true;

//是否关注媒体参数

private boolean favorParameter = false;

//是否忽略http Accept header

private boolean ignoreAcceptHeader = false;

// url 后缀与媒体类型的映射

private Map<String, MediaType> mediaTypes =

new HashMap<String, MediaType>();

//忽略未知的后缀

private boolean ignoreUnknownPathExtensions = true;

// 默认的媒体参数名

private String parameterName = "format”;

可以这样定义一个contentNegotiationManager:

@Configuration

@EnableWebMvc

public class WebConfig extends WebMvcConfigurerAdapter {

@Override

public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {

configurer.favorPathExtension(false).

favorParameter(true).

parameterName("mediaType").

ignoreAcceptHeader(true)

defaultContentType(MediaType.APPLICATION_JSON).

mediaType("xml", MediaType.APPLICATION_XML).

mediaType("json", MediaType.APPLICATION_JSON);

}

}

xml形式的配置:  

<bean id="contentNegotiationManager"

class="org.springframework.web.accept.ContentNegotiationManagerFactoryBean">

<property name="favorPathExtension" value="false" />

<property name="favorParameter" value="true" />

<property name="parameterName" value="mediaType" />

<property name="ignoreAcceptHeader" value="true"/>

<property name="defaultContentType" value="application/json" />

<property name="mediaTypes">

<map>

<entry key="json" value="application/json" />

<entry key="xml" value="application/xml" />

</map>

</property>

</bean>

ContentNegotiation不仅限于Rest风格的http请求,SpringMVC中还有ContentNegotiatingViewResolver完成了viewResolver部分的内容协商功能,有兴趣的小伙伴可以查看部分的源码。 

以上便是今天的全部内容,感谢关注,欢迎小伙伴留言反馈。

往期回顾:

  • Spring Environment Abstraction

  • Spring MVC 异常处理机制

  • Spring 重试机制实现原理

  • Spring Cache的使用及实现原理

  • Spring AOP 模块概述

  • Spring RestTemplate详解

  • Spring类型转换机制

  • Spring中的异步Servlet

  • 玩转Spring bean的终极利器 Spring的注入方式大比拼

  • Spring bean生命周期不可不知的接口

SpringAutowired

内容协商 Spring ContentNegotiation 内容协商 Spring ContentNegotiation

长按,识别二维码,加关注

原文  http://mp.weixin.qq.com/s?__biz=MzI0NTc5Mzk4MQ==&mid=2247483913&idx=1&sn=cebd92a108564f3fe7dc0c29b55b48f7
正文到此结束
Loading...