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)。

相关文章
|
6月前
|
人工智能 JSON 安全
Spring Boot实现无感刷新Token机制
本文深入解析在Spring Boot项目中实现JWT无感刷新Token的机制,涵盖双Token策略、Refresh Token安全性及具体示例代码,帮助开发者提升用户体验与系统安全性。
590 4
|
2月前
|
前端开发 Java 微服务
《深入理解Spring》:Spring、Spring MVC与Spring Boot的深度解析
Spring Framework是Java生态的基石,提供IoC、AOP等核心功能;Spring MVC基于其构建,实现Web层MVC架构;Spring Boot则通过自动配置和内嵌服务器,极大简化了开发与部署。三者层层演进,Spring Boot并非替代,而是对前者的高效封装与增强,适用于微服务与快速开发,而深入理解Spring Framework有助于更好驾驭整体技术栈。
|
9月前
|
JSON 前端开发 Java
微服务——SpringBoot使用归纳——Spring Boot中的MVC支持——@RequestBody
`@RequestBody` 是 Spring 框架中的注解,用于将 HTTP 请求体中的 JSON 数据自动映射为 Java 对象。例如,前端通过 POST 请求发送包含 `username` 和 `password` 的 JSON 数据,后端可通过带有 `@RequestBody` 注解的方法参数接收并处理。此注解适用于传递复杂对象的场景,简化了数据解析过程。与表单提交不同,它主要用于接收 JSON 格式的实体数据。
746 0
|
5月前
|
前端开发 Java API
Spring Cloud Gateway Server Web MVC报错“Unsupported transfer encoding: chunked”解决
本文解析了Spring Cloud Gateway中出现“Unsupported transfer encoding: chunked”错误的原因,指出该问题源于Feign依赖的HTTP客户端与服务端的`chunked`传输编码不兼容,并提供了具体的解决方案。通过规范Feign客户端接口的返回类型,可有效避免该异常,提升系统兼容性与稳定性。
339 0
|
5月前
|
JSON 前端开发 Java
Spring MVC 核心组件与请求处理机制详解
本文解析了 Spring MVC 的核心组件及请求流程,核心组件包括 DispatcherServlet(中央调度)、HandlerMapping(URL 匹配处理器)、HandlerAdapter(执行处理器)、Handler(业务方法)、ViewResolver(视图解析),其中仅 Handler 需开发者实现。 详细描述了请求执行的 7 步流程:请求到达 DispatcherServlet 后,经映射器、适配器找到并执行处理器,再通过视图解析器渲染视图(前后端分离下视图解析可省略)。 介绍了拦截器的使用(实现 HandlerInterceptor 接口 + 配置类)及与过滤器的区别
418 0
|
5月前
|
SQL Java 数据库连接
Spring、SpringMVC 与 MyBatis 核心知识点解析
我梳理的这些内容,涵盖了 Spring、SpringMVC 和 MyBatis 的核心知识点。 在 Spring 中,我了解到 IOC 是控制反转,把对象控制权交容器;DI 是依赖注入,有三种实现方式。Bean 有五种作用域,单例 bean 的线程安全问题及自动装配方式也清晰了。事务基于数据库和 AOP,有失效场景和七种传播行为。AOP 是面向切面编程,动态代理有 JDK 和 CGLIB 两种。 SpringMVC 的 11 步执行流程我烂熟于心,还有那些常用注解的用法。 MyBatis 里,#{} 和 ${} 的区别很关键,获取主键、处理字段与属性名不匹配的方法也掌握了。多表查询、动态
157 0
|
5月前
|
JSON 前端开发 Java
第05课:Spring Boot中的MVC支持
第05课:Spring Boot中的MVC支持
269 0
|
5月前
|
Java Spring 容器
SpringBoot自动配置的原理是什么?
Spring Boot自动配置核心在于@EnableAutoConfiguration注解,它通过@Import导入配置选择器,加载META-INF/spring.factories中定义的自动配置类。这些类根据@Conditional系列注解判断是否生效。但Spring Boot 3.0后已弃用spring.factories,改用新格式的.imports文件进行配置。
898 0
|
6月前
|
人工智能 Java 测试技术
Spring Boot 集成 JUnit 单元测试
本文介绍了在Spring Boot中使用JUnit 5进行单元测试的常用方法与技巧,包括添加依赖、编写测试类、使用@SpringBootTest参数、自动装配测试模块(如JSON、MVC、WebFlux、JDBC等),以及@MockBean和@SpyBean的应用。内容实用,适合Java开发者参考学习。
644 0