SpringFramework之ContentNegotiation内容协商

Easter79
• 阅读 831

    Spring版本5.1.4.release.

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

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

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

    List-1

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

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

    List-2

@Override
public List<MediaType> resolveMediaTypes(NativeWebRequest request) throws HttpMediaTypeNotAcceptableException {
    for (ContentNegotiationStrategy strategy : this.strategies) {
        List<MediaType> mediaTypes = strategy.resolveMediaTypes(request);
        if (mediaTypes.equals(MEDIA_TYPE_ALL_LIST)) {
            continue;
        }
        return mediaTypes;
    }
    return MEDIA_TYPE_ALL_LIST;
}

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

        SpringFramework之ContentNegotiation内容协商

                                                                                    图1

    List-3

public ContentNegotiationManager build() {
    List<ContentNegotiationStrategy> strategies = new ArrayList<>();

    if (this.strategies != null) {
        strategies.addAll(this.strategies);
    }
    else {
        //1
        if (this.favorPathExtension) {
            PathExtensionContentNegotiationStrategy strategy;
            if (this.servletContext != null && !useRegisteredExtensionsOnly()) {
                strategy = new ServletPathExtensionContentNegotiationStrategy(this.servletContext, this.mediaTypes);
            }
            else {
                strategy = new PathExtensionContentNegotiationStrategy(this.mediaTypes);
            }
            strategy.setIgnoreUnknownExtensions(this.ignoreUnknownPathExtensions);
            if (this.useRegisteredExtensionsOnly != null) {
                strategy.setUseRegisteredExtensionsOnly(this.useRegisteredExtensionsOnly);
            }
            strategies.add(strategy);
        }
        //2
        if (this.favorParameter) {
            ParameterContentNegotiationStrategy strategy = new ParameterContentNegotiationStrategy(this.mediaTypes);
            strategy.setParameterName(this.parameterName);
            if (this.useRegisteredExtensionsOnly != null) {
                strategy.setUseRegisteredExtensionsOnly(this.useRegisteredExtensionsOnly);
            }
            else {
                strategy.setUseRegisteredExtensionsOnly(true);  // backwards compatibility
            }
            strategies.add(strategy);
        }
        //3
        if (!this.ignoreAcceptHeader) {
            strategies.add(new HeaderContentNegotiationStrategy());
        }
        //4
        if (this.defaultNegotiationStrategy != null) {
            strategies.add(this.defaultNegotiationStrategy);
        }
    }

    this.contentNegotiationManager = new ContentNegotiationManager(strategies);
    return this.contentNegotiationManager;
}

    如上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

@Override
public List<MediaType> resolveMediaTypes(NativeWebRequest request) throws HttpMediaTypeNotAcceptableException {
    for (ContentNegotiationStrategy strategy : this.strategies) {
        List<MediaType> mediaTypes = strategy.resolveMediaTypes(request);
        if (mediaTypes.equals(MEDIA_TYPE_ALL_LIST)) {
            continue;
        }
        return mediaTypes;
    }
    return MEDIA_TYPE_ALL_LIST;
}

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

点赞
收藏
评论区
推荐文章
Easter79 Easter79
2年前
springmvc详解
spring和struts2的区别:1.springmvc是方法级别的拦截器,struts是类级别的拦截器,springmvc一个方法对应一个request上下文而struts2一个对象对应一个request上下文。springmvc无限接近于0配置,而struts需要大量的配置2.springmvc支持单例开发,二struts最好使用多例开发
Souleigh ✨ Souleigh ✨
2年前
前端性能优化 - 雅虎军规
无论是在工作中,还是在面试中,web前端性能的优化都是很重要的,那么我们进行优化需要从哪些方面入手呢?可以遵循雅虎的前端优化35条军规,这样对于优化有一个比较清晰的方向.35条军规1.尽量减少HTTP请求个数——须权衡2.使用CDN(内容分发网络)3.为文件头指定Expires或CacheControl,使内容具有缓存性。4.避免空的
Easter79 Easter79
2年前
springMVC笔记系列(8)——RequestParam注解
前面的文章介绍过注解@PathVariable,它能够为Rest风格的URL用占位符的方式传递一个参数,但是这个参数并不是真正意义上的请求参数。请求参数怎么处理是本文的主要内容。SpringMVC通过分析处理方法的签名,将HTTP请求信息绑定到处理方法的相应人参中。SpringMVC对控制器处理方法签名的限制是很宽松的,几乎可以按喜欢的任
Stella981 Stella981
2年前
SpringAOP(注解方式实现面向切面编程)之常用Before、After、Around
一、首先在Springmvc.xml文件中引入如下内容(本示例是在ssm框架基础上实现的)  1、引入命名空间xmlns:aop"http://www.springframework.org/schema/aop"  2、在xsi:schemaLocation中引入如下内容(注意看清自己的spring版本号)http://w
Stella981 Stella981
2年前
Spring Boot 406(type=Not Acceptable, status=406)异常解决办法
使用SpringBoot,Controller请求返回的参数类型是ResponseBody,如果请求的时候使用使用配置的默认请求扩展名,例如.html,SpringMVC会抛出一个typeNotAcceptable,status406错误,如下:WhitelabelErrorPageThisapplica
Wesley13 Wesley13
2年前
1、SpringMVC的简介
1.1什么是SpringMVCSpringMVC属于SpringFrameWork的后续产品,已经融合在SpringWebFlow里面。Spring框架提供了构建Web应用程序的全功能MVC模块。使用Spring可插入的 MVC 架构,从而在使用Spring进行WEB开发时,可以选择使用Spring的SpringMVC框架或
Easter79 Easter79
2年前
SpringMVC【开发Controller】详解
前言本文主要是讲解在Controller中的开发,主要的知识点有如下:编码过滤器使用注解开发注解@RequestMapping详解业务方法接收参数字符串转日期重定向和转发返回JSONSpringMVC过滤编码器在SpringMVC的控制
Easter79 Easter79
2年前
SpringMVC 方法三种类型返回值总结,你用过几种?
SpringMVC现在算是Java领域的一个基础性框架了,很多人天天用,可是对于SpringMVC方法的返回值,你又是否完全清楚呢?今天松哥就来和大家聊一聊SpringMVC中四种不同类型的返回值,看看有没有get到你的知识盲点?1\.ModelAndView以前前后端不分的情况下,ModelAn
Stella981 Stella981
2年前
Spring+SpringMVC+MyBatis入门(十五)——SpringMVC注解开发(基础篇)
本文主要内容:(1)商品修改功能开发(2)@RequestMapping(3)Controller类中方法的返回值(4)参数绑定(5)post中文乱码(6)SpringMVC和Struts2的区别1.商品修改功能开发1.1需求操作流程:(1)进入商品查询列表页面;(2)点击修改,进
Stella981 Stella981
2年前
Http 缓存策略
1)浏览器缓存策略浏览器每次发起请求时,先在本地缓存中查找结果以及缓存标识,根据缓存标识来判断是否使用本地缓存。如果缓存有效,则使用本地缓存;否则,则向服务器发起请求并携带缓存标识。根据是否需向服务器发起HTTP请求,将缓存过程划分为两个部分:强制缓存和协商缓存,强缓优先于协商缓存。强缓存,服务器通知浏览器一个缓存时间,在
Easter79
Easter79
Lv1
今生可爱与温柔,每一样都不能少。
文章
2.8k
粉丝
5
获赞
1.2k