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

相关文章

Java中流式并行操作parallelStream的原理和使用方法

《Java中流式并行操作parallelStream的原理和使用方法》本文详细介绍了Java中的并行流(parallelStream)的原理、正确使用方法以及在实际业务中的应用案例,并指出在使用并行流... 目录Java中流式并行操作parallelStream0. 问题的产生1. 什么是parallelS

Java中Redisson 的原理深度解析

《Java中Redisson的原理深度解析》Redisson是一个高性能的Redis客户端,它通过将Redis数据结构映射为Java对象和分布式对象,实现了在Java应用中方便地使用Redis,本文... 目录前言一、核心设计理念二、核心架构与通信层1. 基于 Netty 的异步非阻塞通信2. 编解码器三、

SpringBoot基于注解实现数据库字段回填的完整方案

《SpringBoot基于注解实现数据库字段回填的完整方案》这篇文章主要为大家详细介绍了SpringBoot如何基于注解实现数据库字段回填的相关方法,文中的示例代码讲解详细,感兴趣的小伙伴可以了解... 目录数据库表pom.XMLRelationFieldRelationFieldMapping基础的一些代

一篇文章彻底搞懂macOS如何决定java环境

《一篇文章彻底搞懂macOS如何决定java环境》MacOS作为一个功能强大的操作系统,为开发者提供了丰富的开发工具和框架,下面:本文主要介绍macOS如何决定java环境的相关资料,文中通过代码... 目录方法一:使用 which命令方法二:使用 Java_home工具(Apple 官方推荐)那问题来了,

Java HashMap的底层实现原理深度解析

《JavaHashMap的底层实现原理深度解析》HashMap基于数组+链表+红黑树结构,通过哈希算法和扩容机制优化性能,负载因子与树化阈值平衡效率,是Java开发必备的高效数据结构,本文给大家介绍... 目录一、概述:HashMap的宏观结构二、核心数据结构解析1. 数组(桶数组)2. 链表节点(Node

Java AOP面向切面编程的概念和实现方式

《JavaAOP面向切面编程的概念和实现方式》AOP是面向切面编程,通过动态代理将横切关注点(如日志、事务)与核心业务逻辑分离,提升代码复用性和可维护性,本文给大家介绍JavaAOP面向切面编程的概... 目录一、AOP 是什么?二、AOP 的核心概念与实现方式核心概念实现方式三、Spring AOP 的关

详解SpringBoot+Ehcache使用示例

《详解SpringBoot+Ehcache使用示例》本文介绍了SpringBoot中配置Ehcache、自定义get/set方式,并实际使用缓存的过程,文中通过示例代码介绍的非常详细,对大家的学习或者... 目录摘要概念内存与磁盘持久化存储:配置灵活性:编码示例引入依赖:配置ehcache.XML文件:配置

Java 虚拟线程的创建与使用深度解析

《Java虚拟线程的创建与使用深度解析》虚拟线程是Java19中以预览特性形式引入,Java21起正式发布的轻量级线程,本文给大家介绍Java虚拟线程的创建与使用,感兴趣的朋友一起看看吧... 目录一、虚拟线程简介1.1 什么是虚拟线程?1.2 为什么需要虚拟线程?二、虚拟线程与平台线程对比代码对比示例:三

Java中的.close()举例详解

《Java中的.close()举例详解》.close()方法只适用于通过window.open()打开的弹出窗口,对于浏览器的主窗口,如果没有得到用户允许是不能关闭的,:本文主要介绍Java中的.... 目录当你遇到以下三种情况时,一定要记得使用 .close():用法作用举例如何判断代码中的 input

Spring Gateway动态路由实现方案

《SpringGateway动态路由实现方案》本文主要介绍了SpringGateway动态路由实现方案,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随... 目录前沿何为路由RouteDefinitionRouteLocator工作流程动态路由实现尾巴前沿S