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编译生成多个.class文件的原理和作用

《Java编译生成多个.class文件的原理和作用》作为一名经验丰富的开发者,在Java项目中执行编译后,可能会发现一个.java源文件有时会产生多个.class文件,从技术实现层面详细剖析这一现象... 目录一、内部类机制与.class文件生成成员内部类(常规内部类)局部内部类(方法内部类)匿名内部类二、

SpringBoot实现数据库读写分离的3种方法小结

《SpringBoot实现数据库读写分离的3种方法小结》为了提高系统的读写性能和可用性,读写分离是一种经典的数据库架构模式,在SpringBoot应用中,有多种方式可以实现数据库读写分离,本文将介绍三... 目录一、数据库读写分离概述二、方案一:基于AbstractRoutingDataSource实现动态

Springboot @Autowired和@Resource的区别解析

《Springboot@Autowired和@Resource的区别解析》@Resource是JDK提供的注解,只是Spring在实现上提供了这个注解的功能支持,本文给大家介绍Springboot@... 目录【一】定义【1】@Autowired【2】@Resource【二】区别【1】包含的属性不同【2】@

springboot循环依赖问题案例代码及解决办法

《springboot循环依赖问题案例代码及解决办法》在SpringBoot中,如果两个或多个Bean之间存在循环依赖(即BeanA依赖BeanB,而BeanB又依赖BeanA),会导致Spring的... 目录1. 什么是循环依赖?2. 循环依赖的场景案例3. 解决循环依赖的常见方法方法 1:使用 @La

Java枚举类实现Key-Value映射的多种实现方式

《Java枚举类实现Key-Value映射的多种实现方式》在Java开发中,枚举(Enum)是一种特殊的类,本文将详细介绍Java枚举类实现key-value映射的多种方式,有需要的小伙伴可以根据需要... 目录前言一、基础实现方式1.1 为枚举添加属性和构造方法二、http://www.cppcns.co

Elasticsearch 在 Java 中的使用教程

《Elasticsearch在Java中的使用教程》Elasticsearch是一个分布式搜索和分析引擎,基于ApacheLucene构建,能够实现实时数据的存储、搜索、和分析,它广泛应用于全文... 目录1. Elasticsearch 简介2. 环境准备2.1 安装 Elasticsearch2.2 J

Java中的String.valueOf()和toString()方法区别小结

《Java中的String.valueOf()和toString()方法区别小结》字符串操作是开发者日常编程任务中不可或缺的一部分,转换为字符串是一种常见需求,其中最常见的就是String.value... 目录String.valueOf()方法方法定义方法实现使用示例使用场景toString()方法方法

Java中List的contains()方法的使用小结

《Java中List的contains()方法的使用小结》List的contains()方法用于检查列表中是否包含指定的元素,借助equals()方法进行判断,下面就来介绍Java中List的c... 目录详细展开1. 方法签名2. 工作原理3. 使用示例4. 注意事项总结结论:List 的 contain

Java实现文件图片的预览和下载功能

《Java实现文件图片的预览和下载功能》这篇文章主要为大家详细介绍了如何使用Java实现文件图片的预览和下载功能,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... Java实现文件(图片)的预览和下载 @ApiOperation("访问文件") @GetMapping("

Spring Boot + MyBatis Plus 高效开发实战从入门到进阶优化(推荐)

《SpringBoot+MyBatisPlus高效开发实战从入门到进阶优化(推荐)》本文将详细介绍SpringBoot+MyBatisPlus的完整开发流程,并深入剖析分页查询、批量操作、动... 目录Spring Boot + MyBATis Plus 高效开发实战:从入门到进阶优化1. MyBatis