前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >spring4.1 请求rest接口406问题解决(转换JSON)[通俗易懂]

spring4.1 请求rest接口406问题解决(转换JSON)[通俗易懂]

作者头像
全栈程序员站长
发布2022-07-25 11:51:56
9830
发布2022-07-25 11:51:56
举报
文章被收录于专栏:全栈程序员必看

大家好,又见面了,我是你们的朋友全栈君。

前文说明,本来项目使用的是springmvc 的模式,然后接口都是使用的是 @Controller + @ResponseBody 配置json转换的代码是

代码语言:javascript
复制
  <bean  class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
        <property name="messageConverters">
            <list>
                String 转换器
                <ref bean="stringHttpMessageConverter" />
                JSON 转换器
                <ref bean="jsonHttpMessageConverter" />
            </list>
        </property>
    </bean>
    <bean id="stringHttpMessageConverter" class="org.springframework.http.converter.StringHttpMessageConverter" />

    <bean id="jsonHttpMessageConverter" class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
        <property name="objectMapper">
            <bean class="com.fasterxml.jackson.databind.ObjectMapper">
                <property name="dateFormat">
                    <bean class="java.text.SimpleDateFormat">
                        <constructor-arg type="java.lang.String" value="yyyy-MM-dd HH:mm:ss" />
                    </bean>
                </property>
                <property name="serializationInclusion">
                        <value type="com.fasterxml.jackson.annotation.JsonInclude.Include">NON_NULL</value>
                </property>
            </bean>
        </property>

    </bean> 

后来想直接使用 @RestController 这个注解直接使用

但是接口却一直报 406错误 网上找了很多解决办法,一般都是说的是 JAR缺少,可是我的不缺。spring 是 4.1.4 版本的

代码语言:javascript
复制
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-core</artifactId>
            <version>${jackson.version}</version>
        </dependency>

        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>${jackson.version}</version>
        </dependency>

        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-annotations</artifactId>
            <version>${jackson.version}</version>
        </dependency>

还有说是

代码语言:javascript
复制
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context" xmlns:jee="http://www.springframework.org/schema/jee" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:ehcache="http://ehcache-spring-annotations.googlecode.com/svn/schema/ehcache-spring" xmlns:mvc="http://www.springframework.org/schema/mvc" xsi:schemaLocation=" http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.1.xsd //这里标准的和版本不一致 4.1.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.1.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-4.1.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.1.xsd http://ehcache-spring-annotations.googlecode.com/svn/schema/ehcache-spring http://ehcache-spring-annotations.googlecode.com/svn/schema/ehcache-spring/ehcache-spring-4.1.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.1.xsd " default-lazy-init="true">

我的也是一样的,没问题。

web.xml配置

代码语言:javascript
复制
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" version="3.0">
    <display-name>smk_activity</display-name>
    <!-- webSpringMVC 用户后台页面的配置 -->




    <!-- actionSpringMVC MVC 配置-->
    <servlet>
        <servlet-name>actionSpringMVC</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:config/spring/spring-action-config.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>

    <servlet-mapping>
        <servlet-name>actionSpringMVC</servlet-name>
        <url-pattern>*.ext</url-pattern>
    </servlet-mapping> 
    <servlet-mapping>
        <servlet-name>actionSpringMVC</servlet-name>
        <url-pattern>*.do</url-pattern>
    </servlet-mapping> 

spring 配置

代码语言:javascript
复制
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context" xmlns:jee="http://www.springframework.org/schema/jee" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:ehcache="http://ehcache-spring-annotations.googlecode.com/svn/schema/ehcache-spring" xmlns:mvc="http://www.springframework.org/schema/mvc" xsi:schemaLocation=" http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.1.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.1.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-4.1.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.1.xsd http://ehcache-spring-annotations.googlecode.com/svn/schema/ehcache-spring http://ehcache-spring-annotations.googlecode.com/svn/schema/ehcache-spring/ehcache-spring-4.1.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.1.xsd " default-lazy-init="true">

    <aop:aspectj-autoproxy/>

    <context:annotation-config />

    <context:component-scan base-package="com.smk.activity.action.ext" />

    <context:component-scan base-package="com.smk.activity.action.web" />

    <context:component-scan base-package="com.cat.sy.action" />

    <mvc:interceptors>
        <mvc:interceptor>
            <mvc:mapping path="/**/*.ext" />
            <bean class="com.cat.interceptor.FHandlerInterceptor" />
        </mvc:interceptor> 
        <mvc:interceptor>
            <mvc:mapping path="/**/*.do"/>
            <bean class="com.cat.interceptor.LoginInterceptor"/>
        </mvc:interceptor>
    </mvc:interceptors>



            <bean id="exceptionMessageAdapter" class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerExceptionResolver">  
           <property name="messageConverters">  
               <list>  
                   <!-- Support JSON -->   
                   <bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter"/>  
               </list>  
           </property>  
       </bean>
       <mvc:annotation-driven>  
        <mvc:message-converters>  
                <bean id="jsonHttpMessageConverter" class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
                    <property name="objectMapper">
                        <bean class="com.fasterxml.jackson.databind.ObjectMapper">
                            <property name="dateFormat">
                                <bean class="java.text.SimpleDateFormat">
                                    <constructor-arg type="java.lang.String" value="yyyy-MM-dd HH:mm:ss" />
                                </bean>
                            </property>
                            <property name="serializationInclusion">
                                    <value type="com.fasterxml.jackson.annotation.JsonInclude.Include">NON_NULL</value>
                            </property>
                        </bean>
                    </property>
                </bean> 
                <bean id="stringHttpMessageConverter" class="org.springframework.http.converter.StringHttpMessageConverter" /> 
        </mvc:message-converters>  
    </mvc:annotation-driven>  


    <!-- 视图处理器 -->
    <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/page/" />
        <property name="suffix" value=".jsp" />
    </bean>

但是还是问题依旧的额,所以追踪源码查看问题。

1.追踪请求路径

这里写图片描述
这里写图片描述

其他的就不说了,直接看代码 org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodProcessor 方法 : writeWithMessageConverters

代码语言:javascript
复制
@SuppressWarnings("unchecked")
    protected <T> void writeWithMessageConverters(T returnValue, MethodParameter returnType,
            ServletServerHttpRequest inputMessage, ServletServerHttpResponse outputMessage)
            throws IOException, HttpMediaTypeNotAcceptableException {

        Class<?> returnValueClass = getReturnValueType(returnValue, returnType);
        HttpServletRequest servletRequest = inputMessage.getServletRequest();
        // 获取支持的文件内型。
        List<MediaType> requestedMediaTypes = getAcceptableMediaTypes(servletRequest);
        // 获取系统支付的类型
        List<MediaType> producibleMediaTypes = getProducibleMediaTypes(servletRequest, returnValueClass);

        Set<MediaType> compatibleMediaTypes = new LinkedHashSet<MediaType>();
        for (MediaType requestedType : requestedMediaTypes) {
            for (MediaType producibleType : producibleMediaTypes) {
            //比对是否匹配
                if (requestedType.isCompatibleWith(producibleType)) {
                compatibleMediaTypes.add(getMostSpecificMediaType(requestedType, producibleType));
                }
            }
        }
        //比对不成功,就抛异常出去
        if (compatibleMediaTypes.isEmpty()) {
            if (returnValue != null) {
                throw new HttpMediaTypeNotAcceptableException(producibleMediaTypes);
            }
            return;
        }

        List<MediaType> mediaTypes = new ArrayList<MediaType>(compatibleMediaTypes);
        MediaType.sortBySpecificityAndQuality(mediaTypes);

        MediaType selectedMediaType = null;
        for (MediaType mediaType : mediaTypes) {
            if (mediaType.isConcrete()) {
                selectedMediaType = mediaType;
                break;
            }
            else if (mediaType.equals(MediaType.ALL) || mediaType.equals(MEDIA_TYPE_APPLICATION)) {
                selectedMediaType = MediaType.APPLICATION_OCTET_STREAM;
                break;
            }
        }

        if (selectedMediaType != null) {
            selectedMediaType = selectedMediaType.removeQualityValue();
            for (HttpMessageConverter<?> messageConverter : this.messageConverters) {
                if (messageConverter.canWrite(returnValueClass, selectedMediaType)) {
                    returnValue = this.adviceChain.invoke(returnValue, returnType, selectedMediaType,
                            (Class<HttpMessageConverter<?>>) messageConverter.getClass(), inputMessage, outputMessage);
                    if (returnValue != null) {
                        ((HttpMessageConverter<T>) messageConverter).write(returnValue, selectedMediaType, outputMessage);
                        if (logger.isDebugEnabled()) {
                            logger.debug("Written [" + returnValue + "] as \"" + selectedMediaType + "\" using [" +
                                    messageConverter + "]");
                        }
                    }
                    return;
                }
            }
        }

        if (returnValue != null) {
            throw new HttpMediaTypeNotAcceptableException(this.allSupportedMediaTypes);
        }
    }

List requestedMediaTypes = getAcceptableMediaTypes(servletRequest);

在org.springframework.web.accept.ContentNegotiationManager

代码语言:javascript
复制
    @Override
    public List<MediaType> resolveMediaTypes(NativeWebRequest webRequest) throws HttpMediaTypeNotAcceptableException {
        for (ContentNegotiationStrategy strategy : this.contentNegotiationStrategies) {
            List<MediaType> mediaTypes = strategy.resolveMediaTypes(webRequest);
            if (mediaTypes.isEmpty() || mediaTypes.equals(MEDIA_TYPE_ALL)) {
                continue;
            }
            return mediaTypes;
        }
        return Collections.emptyList();
    }

继续跟踪 List mediaTypes = strategy.resolveMediaTypes(webRequest);

org.springframework.web.accept.AbstractMappingContentNegotiationStrategy

代码语言:javascript
复制
    @Override
    public List<MediaType> resolveMediaTypes(NativeWebRequest webRequest) throws HttpMediaTypeNotAcceptableException {
        String key = getMediaTypeKey(webRequest);
        if (StringUtils.hasText(key)) {
        //本地缓存中查找KEY key=ext 这里就是我们请求的后缀
        //tomcat 本身会自带有一套 MediaType 一般的后缀.html,.json,.js等待会自动转换,但是我们自带的 .ext 是没有的。
            MediaType mediaType = lookupMediaType(key);
            //第一次请求是一定为空
            if (mediaType != null) {
                handleMatch(key, mediaType);
                return Collections.singletonList(mediaType);
            }
            //处理找不到的后缀
            mediaType = handleNoMatch(webRequest, key);
            if (mediaType != null) {
                addMapping(key, mediaType);
                return Collections.singletonList(mediaType);
            }
        }
        return Collections.emptyList();
    }

继续跟踪 org.springframework.web.accept.ServletPathExtensionContentNegotiationStrategy

代码语言:javascript
复制
@Override
    protected MediaType handleNoMatch(NativeWebRequest webRequest, String extension)
            throws HttpMediaTypeNotAcceptableException {

        MediaType mediaType = null;
        if (this.servletContext != null) {
            String mimeType = this.servletContext.getMimeType("file." + extension);
            if (StringUtils.hasText(mimeType)) {
                mediaType = MediaType.parseMediaType(mimeType);
            }
        }
        if (mediaType == null || MediaType.APPLICATION_OCTET_STREAM.equals(mediaType)) {
            MediaType superMediaType = super.handleNoMatch(webRequest, extension);
            if (superMediaType != null) {
                mediaType = superMediaType;
            }
        }
        return mediaType;
    }
这里写图片描述
这里写图片描述

这里就看到了。SPRING4.1版本的 是根据后缀 来生成 mimeType 。这里生成了一个类型application/vnd.novadigm.ext 然而我并不能查到是什么玩意

这时候,回到AbstractMessageConverterMethodProcessor

这里写图片描述
这里写图片描述

看到自带支持的 mimeType 并没有这个类型,所有抛异常出去了。到这里问题就明了了。自定义的.ext 接口,在spring 4.1 以上会抛出这个文件类型不匹配的异常。

现在开始解决。解决的点就在 org.springframework.web.accept.AbstractMappingContentNegotiationStrategy 方法:resolveMediaTypes

代码语言:javascript
复制
@Override
    public List<MediaType> resolveMediaTypes(NativeWebRequest webRequest) throws HttpMediaTypeNotAcceptableException {
        String key = getMediaTypeKey(webRequest);
        if (StringUtils.hasText(key)) {
        //第一次缓存会返回空
            MediaType mediaType = lookupMediaType(key);
            if (mediaType != null) {
                handleMatch(key, mediaType);
                return Collections.singletonList(mediaType);
            }
            //执行这句代码,处理没有匹配的
            mediaType = handleNoMatch(webRequest, key);
            if (mediaType != null) {
                addMapping(key, mediaType);
                return Collections.singletonList(mediaType);
            }
        }
        return Collections.emptyList();
    }

继续看 org.springframework.web.accept.ServletPathExtensionContentNegotiationStrategy

代码语言:javascript
复制
    @Override
    protected MediaType handleNoMatch(NativeWebRequest webRequest, String extension)
            throws HttpMediaTypeNotAcceptableException {

        MediaType mediaType = null;
        if (this.servletContext != null) {
        //从容器中获取 file.ext 的mimeType 
            String mimeType = this.servletContext.getMimeType("file." + extension);
            if (StringUtils.hasText(mimeType)) {
                mediaType = MediaType.parseMediaType(mimeType);
            }
        }
        if (mediaType == null || MediaType.APPLICATION_OCTET_STREAM.equals(mediaType)) {
            MediaType superMediaType = super.handleNoMatch(webRequest, extension);
            if (superMediaType != null) {
                mediaType = superMediaType;
            }
        }
        return mediaType;
    }

从这里就知道了解决办法了,

String mimeType = this.servletContext.getMimeType(“file.” + extension);

根据file.ext 这个key 来获取mimeType

所以在web.xml 中添加 这个对应的类型就能解决问题

代码语言:javascript
复制
//可以添加多个
    <mime-mapping>
        <extension>ext</extension>
        <mime-type>application/json</mime-type>
    </mime-mapping> 
    <mime-mapping>
        <extension>in</extension>
        <mime-type>application/json</mime-type>
    </mime-mapping>

发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/127359.html原文链接:https://javaforall.cn

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022年4月9,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
容器服务
腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档