SpringFramework之ContentNegotiation内容协商

    Spring版本5.1.4.release.

    内容协商是用在Springmvc返回Controller方法结果序列化时使用,而不是解析mvc参数时使用。

    Springmvc支持4种内容协商,拓展名、固定值、Http的头部Accept、请求参数format,那Springmvc中怎么实现的呢,怎么使用已经有很多人分析了,这里来分析下怎么实现的。

    RequestResponseBodyMethodProcessor#handleReturnValue中调用了父类的writeWithMessageConverters,AbstractMessageConverterMethodProcessor#writeWithMessageConverters()中调用了getAcceptableMediaTypes,如下List-1

    List-1

1private List<MediaType> getAcceptableMediaTypes(HttpServletRequest request) 2 throws HttpMediaTypeNotAcceptableException { 3 return this.contentNegotiationManager.resolveMediaTypes(new ServletWebRequest(request)); 4}

    List-2中,ContentNegotiationManager的resolveMediaTypes方法中循坏遍历ContentNegotiationStrategy,分别调用其resolveMediaTypes方法。

    List-2

1@Override 2public List<MediaType> resolveMediaTypes(NativeWebRequest request) throws HttpMediaTypeNotAcceptableException { 3 for (ContentNegotiationStrategy strategy : this.strategies) { 4 List<MediaType> mediaTypes = strategy.resolveMediaTypes(request); 5 if (mediaTypes.equals(MEDIA_TYPE_ALL_LIST)) { 6 continue; 7 } 8 return mediaTypes; 9 } 10 return MEDIA_TYPE_ALL_LIST; 11}

   ContentNegotiationManager是由ContentNegotiationManagerFactoryBean创建的,如下图1所示,ContentNegotiationManagerFactoryBean实现了FactoryBean,通过getObject方法获取创建好后的ContentNegotiationManager,实现接口ServletContextAware是为了获取ServletContext从而构造ServletPathExtensionContentNegotiationStrategy,至于实现InitializingBean则是用afterPropertiesSet进行初始化ContentNegotiationManager

        

                                                                                    图1

    List-3

1public ContentNegotiationManager build() { 2 List<ContentNegotiationStrategy> strategies = new ArrayList<>(); 3 4 if (this.strategies != null) { 5 strategies.addAll(this.strategies); 6 } 7 else { 8 //1 9 if (this.favorPathExtension) { 10 PathExtensionContentNegotiationStrategy strategy; 11 if (this.servletContext != null && !useRegisteredExtensionsOnly()) { 12 strategy = new ServletPathExtensionContentNegotiationStrategy(this.servletContext, this.mediaTypes); 13 } 14 else { 15 strategy = new PathExtensionContentNegotiationStrategy(this.mediaTypes); 16 } 17 strategy.setIgnoreUnknownExtensions(this.ignoreUnknownPathExtensions); 18 if (this.useRegisteredExtensionsOnly != null) { 19 strategy.setUseRegisteredExtensionsOnly(this.useRegisteredExtensionsOnly); 20 } 21 strategies.add(strategy); 22 } 23 //2 24 if (this.favorParameter) { 25 ParameterContentNegotiationStrategy strategy = new ParameterContentNegotiationStrategy(this.mediaTypes); 26 strategy.setParameterName(this.parameterName); 27 if (this.useRegisteredExtensionsOnly != null) { 28 strategy.setUseRegisteredExtensionsOnly(this.useRegisteredExtensionsOnly); 29 } 30 else { 31 strategy.setUseRegisteredExtensionsOnly(true); // backwards compatibility 32 } 33 strategies.add(strategy); 34 } 35 //3 36 if (!this.ignoreAcceptHeader) { 37 strategies.add(new HeaderContentNegotiationStrategy()); 38 } 39 //4 40 if (this.defaultNegotiationStrategy != null) { 41 strategies.add(this.defaultNegotiationStrategy); 42 } 43 } 44 45 this.contentNegotiationManager = new ContentNegotiationManager(strategies); 46 return this.contentNegotiationManager; 47}

    如上List-3所示,

  1. favorPathExtension是true,构造PathExtensionContentNegotiationStrategy,并加到结果结合strategies中,默认ServletPathExtensionContentNegotiationStrategy是不会构造的,除非我们手动的设置
  2. favorParameter是false,如果我们设置为true后,会构造ParameterContentNegotiationStrategy,即我们设置的format=json会生效
  3. ignoreAcceptHeader是false,所以会把HeaderContentNegotiationStrategy加入到结果集合中,即Http头部的Accept
  4. 如果设置了defaultNegotiationStrategy,就会把我们添加的自定义Strategy加入到结果集合中

    如下List-4是ContentNegotiationManager的resolveMediaTypes方法,顺序遍历strategy,如果resolveMediaTypes返回的值不等于MEDIA_TYPE_ALL_LIST,那么就直接返回,结合List-3中添加的顺序,这就是路径拓展第一生效,第二format固定值,第三Http头部的Accept

  List-4

1@Override 2public List<MediaType> resolveMediaTypes(NativeWebRequest request) throws HttpMediaTypeNotAcceptableException { 3 for (ContentNegotiationStrategy strategy : this.strategies) { 4 List<MediaType> mediaTypes = strategy.resolveMediaTypes(request); 5 if (mediaTypes.equals(MEDIA_TYPE_ALL_LIST)) { 6 continue; 7 } 8 return mediaTypes; 9 } 10 return MEDIA_TYPE_ALL_LIST; 11}

    ContentNegotiationStrategy使用了策略模式,在HeaderContentNegotiationStrategy中,从Http头部拿到Accept值后,还对结果按权重进行排序,这是有对应的JSR规范描述这个权重的计算的

点赞
收藏

评论区

加载中...

相关推荐

springmvc详解

spring和struts2的区别:1.springmvc是方法级别的拦截器,struts是类级别的拦截器,springmvc一个方法对应一个request上下文而struts2一个对象对应一个request上下文。springmvc无限接近于0配置,而struts需要大量的配置2.springmvc支持单例开发,二struts最好使用多例开发

springMVC笔记系列(8)——RequestParam注解

前面的文章介绍过注解@PathVariable,它能够为Rest风格的URL用占位符的方式传递一个参数,但是这个参数并不是真正意义上的请求参数。请求参数怎么处理是本文的主要内容。SpringMVC通过分析处理方法的签名,将HTTP请求信息绑定到处理方法的相应人参中。SpringMVC对控制器处理方法签名的限制是很宽松的,几乎可以按喜欢的任

前端性能优化 - 雅虎军规

无论是在工作中,还是在面试中,web前端性能的优化都是很重要的,那么我们进行优化需要从哪些方面入手呢?可以遵循雅虎的前端优化35条军规,这样对于优化有一个比较清晰的方向.35条军规1.尽量减少HTTP请求个数——须权衡2.使用CDN(内容分发网络)3.为文件头指定Expires或CacheControl,使内容具有缓存性。4.避免空的

SpringAOP(注解方式实现面向切面编程)之常用Before、After、Around

一、首先在Springmvc.xml文件中引入如下内容(本示例是在ssm框架基础上实现的)  1、引入命名空间xmlns:aop"http://www.springframework.org/schema/aop"  2、在xsi:schemaLocation中引入如下内容(注意看清自己的spring版本号)http://w

Spring Boot 406(type=Not Acceptable, status=406)异常解决办法

使用SpringBoot,Controller请求返回的参数类型是ResponseBody,如果请求的时候使用使用配置的默认请求扩展名,例如.html,SpringMVC会抛出一个typeNotAcceptable,status406错误,如下:WhitelabelErrorPageThisapplica

1、SpringMVC的简介

1.1什么是SpringMVCSpringMVC属于SpringFrameWork的后续产品,已经融合在SpringWebFlow里面。Spring框架提供了构建Web应用程序的全功能MVC模块。使用Spring可插入的 MVC 架构,从而在使用Spring进行WEB开发时,可以选择使用Spring的SpringMVC框架或