spring4.1 请求rest接口406问题解决(转换JSON)

来源:互联网 发布:机械优化设计 编辑:程序博客网 时间:2024/06/05 18:37

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

  <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 版本的

        <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>

还有说是

<?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配置

<?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 配置

<?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

@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

    @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

    @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

@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

@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

    @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 中添加 这个对应的类型就能解决问题

//可以添加多个    <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>
阅读全文
1 0
原创粉丝点击