26 记一次 SpringMVC 406 The resource identified by this request is only capable of generating responses

本文主要是介绍26 记一次 SpringMVC 406 The resource identified by this request is only capable of generating responses,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

 

问题如下图所示

 

/common/test406.html, 此接口访问的是 SpringMVC 的一个 Controller 的一个 接口, 该接口上面有 @ResponseBody, 意图为作为一个提供数据的数据接口
 

当天晚上[10月18日晚]跟了一下代码, 差不多是临时解决了一下问题, 思路在后面

当天晚上是花了很多时间的, 突然 发现了自己对于 SpringMVC 这个框架的认识还太浅, 虽然 以前也看过脉络的代码, 但是 久而久之就忘记了, 然后 今天重新更了一下大体流程的代码

 

这里总结下, 之前的一个问题, 我注册了一个 mvcContentNegotiationManager, 然后 之后就解决了之前的一个 .html 访问 某一@ResponseBody 接口响应406的问题, 
<mvc:annotation-driven />, 这里的的 AnnotationDrivenBeanDefinitionParser, 想 ac 中注入了一个 

RootBeanDefinition factoryBeanDef = new RootBeanDefinition(ContentNegotiationManagerFactoryBean.class);
factoryBeanDef.setSource(source);
factoryBeanDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
factoryBeanDef.getPropertyValues().add("mediaTypes", getDefaultMediaTypes());


创建的是一个 ContentNegotiationManagerFactoryBean, 然后 之后的时候, 触发了 afterPropertiesSet 初始化, 然后 想contentNegotiationManager[前者] 中增加了 ServletPathExtensionContentNegotiationStrategy, 然后 之后是默认的 HeaderContentNegotiationStrategy
我自己定义的 id为 mvcContentNegotiationManager[后者], 直接使用 类的初始化方法进行初始化, 里面默认有一个 HeaderContentNegotiationStrategy


然后 在这里访问接口的时候, 前者拦截了 .html 然后查询请求需要的类型, ServletPathExtensionContentNegotiationStrategy 在HeaderContentNegotiationStrategy之前, 然后拿到请求的需要的类型为 text/html, 然后响应这边给出的 Content-Type 为  application/json;charset=UTF-8, application/*+json;charset=UTF-8
然后 请求和响应的MimeType 不兼容, 然后 走了后面的  "throw new HttpMediaTypeNotAcceptableException"


然后 后者, 通过 HeaderContentNegotiationStrategy, 在 header 获取客户端接收的类型, 其中有一个; */*吧, 支持所有类型, 这个兼容响应的类型, 然后 就通过了


ContentNegotiationManager. resolveMediaTypes

public List<MediaType> resolveMediaTypes(NativeWebRequest request)throws HttpMediaTypeNotAcceptableException {for (ContentNegotiationStrategy strategy : this.strategies) {List<MediaType> mediaTypes = strategy.resolveMediaTypes(request);if (mediaTypes.isEmpty() || mediaTypes.equals(MEDIA_TYPE_ALL)) {continue;}return mediaTypes;}return Collections.emptyList();
}
if (compatibleMediaTypes.isEmpty()) {if (returnValue != null) {throw new HttpMediaTypeNotAcceptableException(producibleMediaTypes);}return;
}




然后 这里还有一个需要注意的问题, 容器的初始化流程, 
<mvc:annotation-driven />
<bean id="mvcContentNegotiationManager" class="org.springframework.web.accept.ContentNegotiationManager"/>
这样的顺序是没有问题的, 因为后来的 beanDef 覆盖了前面的 AnnotationDrivenBeanDefinitionParser 中向 ac 中注册的beanDef, 
但是 反过来, 执行的最终容器中保留的就是, ContentNegotiationManagerFactoryBean 计算得到的 contentNegotiationManager, 然后 因此, 之前的 406 会出现


此问题, 另外一种解决思路是, 去掉请求时候的后缀, 或者更换后缀, 此ServletPathExtensionContentNegotiationStrategy 默认支持的后缀来自于, 其依赖的 ContentNegotiationManager 为["xml" -> "application/xml", "json" -> "application/json"] 以及handleNoMatch的时候支持的 FileTypeMap. getDefaultFileTypeMap
默认的 ContentNegotiationManager初始化部分具体的代码请参见 : AnnotationDrivenBeanDefinitionParser. getContentNegotiationManager

 


http://www.codeweblog.com/%E8%A7%A3%E5%86%B3springmvc%E4%B8%AD%E7%9A%84-could-not-find-acceptable-represent/


此问题, 还有一个奇葩的插曲, 我按照上面的链接的方式修改了之后, 我这边没有问题了, 然后 同事那边更新了之后, 缺访问不了静态资源了, 然后 我clean了一下浏览器的缓存, 然后 刷新发现依然能够拿到静态资源
这里不能拿到静态资源也不难理解, 相关的请求被 dispatcherServlet, 拦截了, 然后 被internalResourceViewResolver拦截了, 然后 在/WEB-INF/pages 下面找资源, 然而 真正的资源在外层的 webapps/$CONTEXT_PATH/static
然后 dispatcherServlet 拦截的 url-pattern 为 *.html 的时候, 静态资源相关请求是被 Tomcat 的 DefaultServlet 来处理
然后 之后的时候, 同事更新了一下代码, 增加了 MappingJackson2HttpMessageConverter 的配置, 然后加入了一些依赖之后, 就能够通过 .html 访问道接口, 拿到正确的数据了, 


详细的细节, 以及追一下, 如下图的代码, 

 

--------------------------------------------------------------------------------


最后的时候, 说一下, 构造此种情况的方法
1. web.xml 中的配置

	<servlet><servlet-name>springDispatcherServlet</servlet-name><servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class><init-param><param-name>contextConfigLocation</param-name><param-value>classpath:spring/applicationContext-mvc.xml</param-value></init-param><load-on-startup>2</load-on-startup></servlet><servlet-mapping><servlet-name>springDispatcherServlet</servlet-name><url-pattern>*.html</url-pattern></servlet-mapping>


2. applicationContext-mvc.xml 中添加 <mvc:annotation-driven />
3. 增加 @Controller

 

    @RequestMapping(value = "test406")@ResponseBodypublic JSONObject test406(HttpServletRequest request, HttpServletResponse response) throws Exception {JSONObject result = new JSONObject();result.put("k", "v");return result;}

 

======================= add at 2018.11.28 ======================= 

上周四的时候, 我们同事 同样是碰到了一个这样的 406 的问题, 然后 我就感觉之前遇到过 

然后 之后今天的时候, 看了一下, 跟了一下流程 

 

示例代码如下 

直接 发送http请求, 我们期望拿到的是 一个正确的 json 结果, 但是 实际上返回的是 一个 406 的错误页面 

然后 假设 吧 "doc.list" 更新为 "doc.list1", 然后 就能够拿到 期望的正确页面, 那么这个是怎么回事呢?? 

 

我们这里的 mvcContentNegotiationManager 属于上面的前者, 使用的是 AnnotationDrivenBeanDefinitionParser 处理的过程中注入的 mvcContentNegotiationManager, 优先使用 ServletPathExtensionContentNegotiationStrategy 解析客户端请求的 mimeType, 然后再尝试 使用 HeaderContentNegotiationStrategy 来解析 客户端请求的 mimeType 

使用"doc.list", 调试发现, 使用 ServletPathExtensionContentNegotiationStrategy 首先 lookupMediaType 没有找到 "list" 对应的 mimeType, 然后 在 handleNoMatch 的补偿中 在 ServletContext -> StandardContext 中找到了 list 对应的 mimeType 为 "text/plain", 然后 就返回回去了

然后 实际响应的类型是 "application/json" 和 "application/*+json", 请求类型 和响应类型 不兼容 走了后面的  "throw new HttpMediaTypeNotAcceptableException"

 

使用"doc.list1", 调试发现, 两个 NegotiationStrategy 都没有从请求中找到合适的类型, 然后使用了 默认的 "*/*", 接受所有的类型 

 

AbstractMappingContentNegotiationStrategy. resolveMediaTypeKey

public List<MediaType> resolveMediaTypeKey(NativeWebRequest webRequest, String key)throws HttpMediaTypeNotAcceptableException {if (StringUtils.hasText(key)) {MediaType mediaType = lookupMediaType(key);if (mediaType != null) {handleMatch(key, mediaType);return Collections.singletonList(mediaType);}mediaType = handleNoMatch(webRequest, key);        // line : 78if (mediaType != null) {addMapping(key, mediaType);return Collections.singletonList(mediaType);}}return Collections.emptyList();
}

 

 

参考连接

http://www.codeweblog.com/%E8%A7%A3%E5%86%B3springmvc%E4%B8%AD%E7%9A%84-could-not-find-acceptable-represent/

 

 

这篇关于26 记一次 SpringMVC 406 The resource identified by this request is only capable of generating responses的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1010983

相关文章

Spring Boot中WebSocket常用使用方法详解

《SpringBoot中WebSocket常用使用方法详解》本文从WebSocket的基础概念出发,详细介绍了SpringBoot集成WebSocket的步骤,并重点讲解了常用的使用方法,包括简单消... 目录一、WebSocket基础概念1.1 什么是WebSocket1.2 WebSocket与HTTP

SpringBoot+Docker+Graylog 如何让错误自动报警

《SpringBoot+Docker+Graylog如何让错误自动报警》SpringBoot默认使用SLF4J与Logback,支持多日志级别和配置方式,可输出到控制台、文件及远程服务器,集成ELK... 目录01 Spring Boot 默认日志框架解析02 Spring Boot 日志级别详解03 Sp

java中反射Reflection的4个作用详解

《java中反射Reflection的4个作用详解》反射Reflection是Java等编程语言中的一个重要特性,它允许程序在运行时进行自我检查和对内部成员(如字段、方法、类等)的操作,本文将详细介绍... 目录作用1、在运行时判断任意一个对象所属的类作用2、在运行时构造任意一个类的对象作用3、在运行时判断

java如何解压zip压缩包

《java如何解压zip压缩包》:本文主要介绍java如何解压zip压缩包问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录Java解压zip压缩包实例代码结果如下总结java解压zip压缩包坐在旁边的小伙伴问我怎么用 java 将服务器上的压缩文件解压出来,

SpringBoot中SM2公钥加密、私钥解密的实现示例详解

《SpringBoot中SM2公钥加密、私钥解密的实现示例详解》本文介绍了如何在SpringBoot项目中实现SM2公钥加密和私钥解密的功能,通过使用Hutool库和BouncyCastle依赖,简化... 目录一、前言1、加密信息(示例)2、加密结果(示例)二、实现代码1、yml文件配置2、创建SM2工具

Spring WebFlux 与 WebClient 使用指南及最佳实践

《SpringWebFlux与WebClient使用指南及最佳实践》WebClient是SpringWebFlux模块提供的非阻塞、响应式HTTP客户端,基于ProjectReactor实现,... 目录Spring WebFlux 与 WebClient 使用指南1. WebClient 概述2. 核心依

Spring Boot @RestControllerAdvice全局异常处理最佳实践

《SpringBoot@RestControllerAdvice全局异常处理最佳实践》本文详解SpringBoot中通过@RestControllerAdvice实现全局异常处理,强调代码复用、统... 目录前言一、为什么要使用全局异常处理?二、核心注解解析1. @RestControllerAdvice2

Spring IoC 容器的使用详解(最新整理)

《SpringIoC容器的使用详解(最新整理)》文章介绍了Spring框架中的应用分层思想与IoC容器原理,通过分层解耦业务逻辑、数据访问等模块,IoC容器利用@Component注解管理Bean... 目录1. 应用分层2. IoC 的介绍3. IoC 容器的使用3.1. bean 的存储3.2. 方法注

Spring事务传播机制最佳实践

《Spring事务传播机制最佳实践》Spring的事务传播机制为我们提供了优雅的解决方案,本文将带您深入理解这一机制,掌握不同场景下的最佳实践,感兴趣的朋友一起看看吧... 目录1. 什么是事务传播行为2. Spring支持的七种事务传播行为2.1 REQUIRED(默认)2.2 SUPPORTS2

怎样通过分析GC日志来定位Java进程的内存问题

《怎样通过分析GC日志来定位Java进程的内存问题》:本文主要介绍怎样通过分析GC日志来定位Java进程的内存问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、GC 日志基础配置1. 启用详细 GC 日志2. 不同收集器的日志格式二、关键指标与分析维度1.