Spring-web源码解析之ContentNegotiationStrategy

创建ContentNegotiationStrategy组件源码,参考:org.springframework.web.accept.ContentNegotiationManagerFactoryBean#afterPropertiesSet

Spring-web源码解析之ContentNegotiationStrategy

基于4.1.7.RELEASE

request和mediatypes解析的策略类,其唯一的一个接口是

List<MediaType> resolveMediaTypes(NativeWebRequest webRequest) throws HttpMediaTypeNotAcceptableException;
该接口负责将给与的request解析出对应的MediaType来,其实现有以下几种

FixedContentNegotiationStrategy
固定类型解析 : 返回固定的MediaType,每个类都有一个defaultContentType,在构造函数时需要传入默认的MediaType类型,其接口实现方式如下
@Override
public List<MediaType> resolveMediaTypes(NativeWebRequest webRequest) {
return Collections.singletonList(this.defaultContentType);
}

HeaderContentNegotiationStrategy
accept Header解析 : 负责解析request头中的accept,接口实现如下
@Override
public List<MediaType> resolveMediaTypes(NativeWebRequest webRequest) throws HttpMediaTypeNotAcceptableException {
String acceptHeader = webRequest.getHeader(ACCEPT_HEADER);
try {
if (StringUtils.hasText(acceptHeader)) {
List<MediaType> mediaTypes = MediaType.parseMediaTypes(acceptHeader);
MediaType.sortBySpecificityAndQuality(mediaTypes);
return mediaTypes;
}
}
catch (InvalidMediaTypeException ex) {
throw ;
}
return Collections.emptyList();
}

这里首先从request中取出Accept对应的字段,然后解析将其包装成MeidaType,排完序后返回MediaType列表。下面举例来说明:
比如我有一个请求  Accept是这样  
Accept:image/webp,image/*,*/*;q=0.8
在上面的步骤中,首先会取出Accept对应的字段交给MediaType.parseMediaTypes进行包装,根据正则表达式 “ ,\\s* ”进行分割,分割后字符串变成了如下形式[imgage/webp] [image/*] [*/*;q=0.8]
然后逐个对数组元素进行判断,判断规则如下
1 根据 “;” 符号进行拆分
2 拆分出的第一部分再根据  “/“ 进行拆分
3 判断步骤2中拆分出的两部分是否符合  */xxx 的模式,如果是,则抛出异常,这里只允许   */*,  xx/* , xx/xx  的模式 (type/subType)
4 对步骤1中第二部分根据 ”=“ 号拆分,将其封装进MediaType的parameterMap中
5 将步骤3中的type和subType封装进MediaType

到此处理完毕之后就返回解析出的MediaTypes。

ParameterContentNegotiationStrategy
parameter解析 : 根据request中的参数来判断mediaType的类型,默认的参数名为format,在其构造函数中需要传递一个mediaType的Map,在解析format时,format对应的值就会在这个map里寻找匹配MediaType。其参数名可以使用setParameterName方法注入修改。
举例: http://xxx.alibaba-inc.com/xx?format=json  时,会根据format的值json,去寻找json对应的MediaType类型。
@Override
protected String getMediaTypeKey(NativeWebRequest webRequest) {
return webRequest.getParameter(this.parameterName);
}

根据MediaTypeKey去寻找MediaType。

PathExtensionContentNegotiationStrategy
路径名解析 : 根据请求路径的后缀名来判断用哪种MediaType,默认忽略未知的路径扩展名,比如说我们常见的 xx.html,xx.json ,里的.html,.json都是已知的路径扩展名,对于程序来说,只要没有匹配上就属于未知的扩展名。
在寻找路径名匹配的过程中,如果在构造函数中的Map中没有找到MediaType,会采用JAF作为备用机制来查询MediaType。
@Override
protected String getMediaTypeKey(NativeWebRequest webRequest) {
HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class);
if (servletRequest == null) {
return null;
}
String path = urlPathHelper.getLookupPathForRequest(servletRequest);
String filename = WebUtils.extractFullFilenameFromUrlPath(path);
String extension = StringUtils.getFilenameExtension(filename);
return (StringUtils.hasText(extension)) ? extension.toLowerCase(Locale.ENGLISH) : null;
}

根据MediaTypeKey去寻找MediaType。

ServletPathExtensionContentNegotiationStrategy
属于4的扩展,作为4的一种备用机制使用ServletContext.getMIMEType来匹配MediaType,备用机制的实现是在handlerNoMatch方法里,如果是application/octet-stream类型(比如上传文件),则不采用该备用机制。

总结:4种策略  固定类型,accept Header解析,parameter解析,路径扩展名解析。
————————————————
版权声明:本文为CSDN博主「子沭」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/ktlifeng/article/details/50570128

SpringMVC源码解析-ContentNegotiationStrategy

说白了,这个接口就是想知道你(客户端)需要什么类型(MediaType)的数据。不是针对Content-Type的,因为没有解析charset。

/**
* 处理请求中的媒体类型的策略接口
*/
public interface ContentNegotiationStrategy {
/**
* 将给定的请求解析为媒体类型列表
* 返回的 List 首先按照 specificity 参数排序,其次按照 quality 参数排序
*
* webRequest: 当前的请求
* 返回请求的媒体类型或者是一个空的 List
* 如果请求的媒体类型不能被解析则抛出 HttpMediaTypeNotAcceptableException 异常
*/
List<MediaType> resolveMediaTypes(NativeWebRequest webRequest)
throws HttpMediaTypeNotAcceptableException;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

SpringMVC 默认加载两个该接口的实现类:
ServletPathExtensionContentNegotiationStrategy–根据文件扩展名。
HeaderContentNegotiationStrategy–根据HTTP Header里的Accept字段。

HeaderContentNegotiationStrategy 负责解析Http Request Header中的Accept

/**
* 检查请求头 Accept 的 ContentNegotiationStrategy 的实现类
*/
public class HeaderContentNegotiationStrategy implements ContentNegotiationStrategy {

/**
* 如果 Accept 请求头不能被解析则抛出 HttpMediaTypeNotAcceptableException 异常
*/
@Override
public List<MediaType> resolveMediaTypes(NativeWebRequest request)
throws HttpMediaTypeNotAcceptableException {
//获得请求头 Accept 的值
// 形如: "Accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8"
String[] headerValueArray = request.getHeaderValues(HttpHeaders.ACCEPT);
if (headerValueArray == null) {
return Collections.<MediaType>emptyList();
}

List<String> headerValues = Arrays.asList(headerValueArray);
try {
// 将上述媒体类型组成的字符串分离出来
List<MediaType> mediaTypes = MediaType.parseMediaTypes(headerValues);
// 将媒体类型排序
MediaType.sortBySpecificityAndQuality(mediaTypes);
return mediaTypes;
}
catch (InvalidMediaTypeException ex) {
throw new HttpMediaTypeNotAcceptableException(
"Could not parse ‘Accept‘ header " + headerValues + ": " + ex.getMessage());
}
}

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
调用关系:

private final ContentNegotiationManager contentNegotiationManager;
contentNegotiationManager 保存了ContentNegotiationStrategy 的实现类
private final List<ContentNegotiationStrategy> strategies = new ArrayList<ContentNegotiationStrategy>();

同时也可以看到如果没有传递Accept,则默认使用MediaType.ALL 也就是*/*

参考:
http://blog.csdn.net/ktlifeng/article/details/50570128

————————————————
版权声明:本文为CSDN博主「N3verL4nd」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/x_iya/article/details/78114101

原文地址:https://www.cnblogs.com/hfultrastrong/p/12175259.html

时间: 2024-10-21 03:08:10

Spring-web源码解析之ContentNegotiationStrategy的相关文章

spring mvc源码解析

1.从DispatcherServlet开始     与很多使用广泛的MVC框架一样,SpringMVC使用的是FrontController模式,所有的设计都围绕DispatcherServlet 为中心来展开的.见下图,所有请求从DispatcherServlet进入,DispatcherServlet根据配置好的映射策略确定处理的 Controller,Controller处理完成返回ModelAndView,DispatcherServlet根据配置好的视图策略确定处理的 View,由V

spring boot 源码解析 启动流程

spring boot 源码解析 启动流程 在面试过程中经常被问到过spring boot的启动流程,今天就翻一下源码整体看一下: 首先,新建一个启动类,可以看到是首先调用的SpringApplication的静态方法run @SpringBootApplication public class SourceReadApplillcation { public static void main(String[] args) { SpringApplication.run(SourceReadAp

Spring IoC源码解析之getBean

一.实例化所有的非懒加载的单实例Bean 从org.springframework.context.support.AbstractApplicationContext#refresh方法开发,进入到实例化所有的非懒加载的单实例Bean的finishBeanFactoryInitialization(beanFactory)的方法: protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory bea

Spring IoC源码解析——Bean的创建和初始化

Spring介绍 Spring(http://spring.io/)是一个轻量级的Java 开发框架,同时也是轻量级的IoC和AOP的容器框架,主要是针对JavaBean的生命周期进行管理的轻量级容器,可以单独使用,也可以和Struts框架,MyBatis框架等组合使用. IoC介绍 IoC是什么 Ioc-Inversion of Control,即"控制反转",不是什么技术,而是一种设计思想.在Java开发中,Ioc意味着将你设计好的对象交给容器控制,而不是传统的在你的对象内部直接控

Spring事务源码解析(二)获取增强

在上一篇文章@EnableTransactionManagement注解解析中,我们搭建了源码阅读的环境,以及解析了开启Spring事务功能的注解@EnableTransactionManagement的实现逻辑 在进行接下来的源码解析之前我想大家应该知道,当我们使用传统的jdbc应用事务的时候是不是做了如下操作: 开启事务 save.update.delete等操作 出现异常进行回滚 正常情况提交事务 而在Spring中我们好像只需要关心第三步,也就是我们的业务,而其他的操作都不需要关心.那么

Spring AOP源码解析——专治你不会看源码的坏毛病!

昨天有个大牛说我啰嗦,眼光比较细碎,看不到重点.太他爷爷的有道理了!要说看人品,还是女孩子强一些. 原来记得看到一个男孩子的抱怨,说怎么两人刚刚开始在一起,女孩子在心里就已经和他过完了一辈子.哥哥们,不想这么远行吗?看看何洁,看看带着俩娃跳楼的妈妈. 所以现在的女孩子是很明白的,有些男孩子个子不高,其貌不扬,但是一看那人品气质就知道能找个不错的女盆友.不过要说看人的技术能力,男孩子确实更胜一筹,咱得努力了. 总结一下要形成的习惯: 有空时隔一段时间要做几道算法题,C语言和JAVA都可以,主要是训

spring boot 源码解析52-actuate中MVCEndPoint解析

今天有个bie项目的jolokia的endpoint不能访问,调试源码发现:endpoint.enabled的开关导致的. 前言之前的几篇文章分析了spring boot 中有关endpoint的实现,细心的朋友可以发现,在org.springframework.boot.actuate.endpoint.mvc 包下也有一系列的xxxEndpoint,这又是为什么呢? 原因是: 我们很多情况下,都是访问接口的方式获取应用的监控,之前的分析是其实现的底层,要想实现通过接口访问,还需要对其进行包装

spring session源码解析

模块划分 core部分代码 存储实现部分部分: jdbc实现 具体存储的实现类 例如:org.springframework.session.jdbc.JdbcOperationsSessionRepository 支持jdbc访问的都可以作为存储介质 JdbcOperationsSessionRepository通过sql来访问后端存储: private static final String CREATE_SESSION_QUERY = "INSERT INTO %TABLE_NAME%(P

spring mvc源码解析(一)

首先简单了解spring mvc使用 首先搭建一个maven的web工程,最简单的mvc工程只需要依赖 <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.1.7.RELEASE</version> </dependency> //注意配置作用域,防止和tomc