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

2022-07-25 11:51:56 浏览数 (1)

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

前文说明,本来项目使用的是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

0 人点赞