ContentNegotiation内容协商机制(一)---Spring MVC内置支持的4种内容协商方式【享学Spring MVC】(下)

简介: ContentNegotiation内容协商机制(一)---Spring MVC内置支持的4种内容协商方式【享学Spring MVC】(下)

produces使用固然也比较简单,针对上面报错406的原因,我简单解释如下。


原因:


1、先解析请求的媒体类型:1.xml解析出来的MediaType是application/xml

2、拿着这个MediaType(当然还有URL、请求Method等所有)去匹配HandlerMethod的时候会发现producers匹配不上

3、匹配不上就交给RequestMappingInfoHandlerMapping.handleNoMatch()处理:

RequestMappingInfoHandlerMapping:
  @Override
  protected HandlerMethod handleNoMatch(...) {
    if (helper.hasConsumesMismatch()) {
      ...
      throw new HttpMediaTypeNotSupportedException(contentType, new ArrayList<>(mediaTypes));
    }
    // 抛出异常:HttpMediaTypeNotAcceptableException
    if (helper.hasProducesMismatch()) {
      Set<MediaType> mediaTypes = helper.getProducibleMediaTypes();
      throw new HttpMediaTypeNotAcceptableException(new ArrayList<>(mediaTypes));
    }
  } 


4、抛出异常后最终交给DispatcherServlet.processHandlerException()去处理这个异常,转换到Http状态码


会调用所有的handlerExceptionResolvers来处理这个异常,本处会被DefaultHandlerExceptionResolver最终处理。最终处理代码如下(406状态码):

  protected ModelAndView handleHttpMediaTypeNotAcceptable(HttpMediaTypeNotAcceptableException ex,
      HttpServletRequest request, HttpServletResponse response, @Nullable Object handler) throws IOException {
    response.sendError(HttpServletResponse.SC_NOT_ACCEPTABLE);
    return new ModelAndView();
  }


Spring MVC默认注册的异常处理器是如下3个:



原理


有了关于Accept的原理描述,理解它就非常简单了。因为指定了produces属性,所以getProducibleMediaTypes()方法在拿服务端支持的媒体类型时:



protected List<MediaType> getProducibleMediaTypes( ... ){
  Set<MediaType> mediaTypes = (Set<MediaType>) request.getAttribute(HandlerMapping.PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE);
  if (!CollectionUtils.isEmpty(mediaTypes)) {
    return new ArrayList<>(mediaTypes);
  }
  ...
}


因为设置了producers,所以代码第一句就能拿到值了(后面的协商机制完全同上)。


备注:若produces属性你要指定的非常多,建议可以使用!xxx语法,它是支持这种语法(排除语法)的~


优缺点:


  • 优点:使用简单,天然支持
  • 缺点:让HandlerMethod处理器缺失灵活性


Spring Boot默认异常消息处理


再回到开头的Spring Boot为何对异常消息,浏览器和postman的展示不一样。这就是Spring Boot默认的对异常处理方式:它使用的就是基于 固定类型(produces)实现的内容协商。


Spirng Boot出现异常信息时候,会默认访问/error,它的处理类是:BasicErrorController


@Controller
@RequestMapping("${server.error.path:${error.path:/error}}")
public class BasicErrorController extends AbstractErrorController {
  ...
  // 处理类浏览器
  @RequestMapping(produces = "text/html")
  public ModelAndView errorHtml(HttpServletRequest request, HttpServletResponse response) {
    ... 
    return (modelAndView != null ? modelAndView : new ModelAndView("error", model));
  }
  // 处理restful/json方式
  @RequestMapping
  @ResponseBody
  public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) {
    Map<String, Object> body = getErrorAttributes(request, isIncludeStackTrace(request, MediaType.ALL));
    HttpStatus status = getStatus(request);
    return new ResponseEntity<Map<String, Object>>(body, status);
  }
  ...
}


有了上面的解释,对这块代码的理解应该就没有盲点了~


总结



内容协商在RESTful流行的今天还是非常重要的一块内容,它对于提升用户体验,提升效率和降低维护成本都有不可忽视的作用,注意它三的优先级为:后缀 > 请求参数 > HTTP首部Accept


一般情况下,我们为了通用都会使用基于Http的内容协商(Accept),但在实际应用中其实很少用它,因为不同的浏览器可能导致不同的行为(比如Chrome和Firefox就很不一样),所以为了保证“稳定性”一般都选择使用方案二或方案三(比如Spring的官方doc)。

相关文章
|
19天前
|
缓存 前端开发 Java
【Java面试题汇总】Spring,SpringBoot,SpringMVC,Mybatis,JavaWeb篇(2023版)
Soring Boot的起步依赖、启动流程、自动装配、常用的注解、Spring MVC的执行流程、对MVC的理解、RestFull风格、为什么service层要写接口、MyBatis的缓存机制、$和#有什么区别、resultType和resultMap区别、cookie和session的区别是什么?session的工作原理
【Java面试题汇总】Spring,SpringBoot,SpringMVC,Mybatis,JavaWeb篇(2023版)
|
2月前
|
Java 数据库连接 Spring
后端框架入门超详细 三部曲 Spring 、SpringMVC、Mybatis、SSM框架整合案例 【爆肝整理五万字】
文章是关于Spring、SpringMVC、Mybatis三个后端框架的超详细入门教程,包括基础知识讲解、代码案例及SSM框架整合的实战应用,旨在帮助读者全面理解并掌握这些框架的使用。
后端框架入门超详细 三部曲 Spring 、SpringMVC、Mybatis、SSM框架整合案例 【爆肝整理五万字】
|
2月前
|
Java 开发工具 Spring
Spring的Factories机制介绍
Spring的Factories机制介绍
39 1
|
3月前
|
前端开发 Java 应用服务中间件
我以为我对Spring MVC很了解,直到我遇到了...
所有人都知道Spring MVC是是开发的,却鲜有人知道Spring MVC的理论基础来自于1978 年提出MVC模式的一个老头子,他就是Trygve Mikkjel Heyerdahl Reenskaug,挪威计算机科学家,名誉教授。Trygve Reenskaug的MVC架构思想早期用于图形用户界面(GUI) 的软件设计,他对MVC是这样解释的。MVC 被认为是解决用户控制大型复杂数据集问题的通用解决方案。最困难的部分是为不同的架构组件想出好的名字。模型-视图-编辑器是第一个。
115 1
我以为我对Spring MVC很了解,直到我遇到了...
|
2月前
|
前端开发 Java Spring
Java 新手入门:Spring Boot 轻松整合 Spring 和 Spring MVC!
Java 新手入门:Spring Boot 轻松整合 Spring 和 Spring MVC!
48 0
|
3月前
|
安全 Java API
构建基于Spring Boot的REST API安全机制
构建基于Spring Boot的REST API安全机制
|
3月前
|
前端开发 Java Spring
Spring MVC中使用ModelAndView传递数据
Spring MVC中使用ModelAndView传递数据
|
3月前
|
XML 前端开发 Java
Spring Boot与Spring MVC的区别和联系
Spring Boot与Spring MVC的区别和联系
|
3月前
|
Java Spring
解析Spring Boot中的事务管理机制
解析Spring Boot中的事务管理机制
|
3月前
|
缓存 安全 Java
Spring Boot中的自动配置机制详解
Spring Boot中的自动配置机制详解
下一篇
无影云桌面