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 实用工具类Spring 的 AnnotationUtils详解

《Java实用工具类Spring的AnnotationUtils详解》Spring框架提供了一个强大的注解工具类org.springframework.core.annotation.Annot... 目录前言一、AnnotationUtils 的常用方法二、常见应用场景三、与 JDK 原生注解 API 的

Java controller接口出入参时间序列化转换操作方法(两种)

《Javacontroller接口出入参时间序列化转换操作方法(两种)》:本文主要介绍Javacontroller接口出入参时间序列化转换操作方法,本文给大家列举两种简单方法,感兴趣的朋友一起看... 目录方式一、使用注解方式二、统一配置场景:在controller编写的接口,在前后端交互过程中一般都会涉及

Java中的StringBuilder之如何高效构建字符串

《Java中的StringBuilder之如何高效构建字符串》本文将深入浅出地介绍StringBuilder的使用方法、性能优势以及相关字符串处理技术,结合代码示例帮助读者更好地理解和应用,希望对大家... 目录关键点什么是 StringBuilder?为什么需要 StringBuilder?如何使用 St

使用Java将各种数据写入Excel表格的操作示例

《使用Java将各种数据写入Excel表格的操作示例》在数据处理与管理领域,Excel凭借其强大的功能和广泛的应用,成为了数据存储与展示的重要工具,在Java开发过程中,常常需要将不同类型的数据,本文... 目录前言安装免费Java库1. 写入文本、或数值到 Excel单元格2. 写入数组到 Excel表格

Java并发编程之如何优雅关闭钩子Shutdown Hook

《Java并发编程之如何优雅关闭钩子ShutdownHook》这篇文章主要为大家详细介绍了Java如何实现优雅关闭钩子ShutdownHook,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起... 目录关闭钩子简介关闭钩子应用场景数据库连接实战演示使用关闭钩子的注意事项开源框架中的关闭钩子机制1.

Maven中引入 springboot 相关依赖的方式(最新推荐)

《Maven中引入springboot相关依赖的方式(最新推荐)》:本文主要介绍Maven中引入springboot相关依赖的方式(最新推荐),本文给大家介绍的非常详细,对大家的学习或工作具有... 目录Maven中引入 springboot 相关依赖的方式1. 不使用版本管理(不推荐)2、使用版本管理(推

Java 中的 @SneakyThrows 注解使用方法(简化异常处理的利与弊)

《Java中的@SneakyThrows注解使用方法(简化异常处理的利与弊)》为了简化异常处理,Lombok提供了一个强大的注解@SneakyThrows,本文将详细介绍@SneakyThro... 目录1. @SneakyThrows 简介 1.1 什么是 Lombok?2. @SneakyThrows

在 Spring Boot 中实现异常处理最佳实践

《在SpringBoot中实现异常处理最佳实践》本文介绍如何在SpringBoot中实现异常处理,涵盖核心概念、实现方法、与先前查询的集成、性能分析、常见问题和最佳实践,感兴趣的朋友一起看看吧... 目录一、Spring Boot 异常处理的背景与核心概念1.1 为什么需要异常处理?1.2 Spring B

如何在 Spring Boot 中实现 FreeMarker 模板

《如何在SpringBoot中实现FreeMarker模板》FreeMarker是一种功能强大、轻量级的模板引擎,用于在Java应用中生成动态文本输出(如HTML、XML、邮件内容等),本文... 目录什么是 FreeMarker 模板?在 Spring Boot 中实现 FreeMarker 模板1. 环

SpringMVC 通过ajax 前后端数据交互的实现方法

《SpringMVC通过ajax前后端数据交互的实现方法》:本文主要介绍SpringMVC通过ajax前后端数据交互的实现方法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价... 在前端的开发过程中,经常在html页面通过AJAX进行前后端数据的交互,SpringMVC的controll