前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >SpringMVC配置Tomcat返回406问题探索

SpringMVC配置Tomcat返回406问题探索

作者头像
Dylan Liu
发布2019-07-01 13:12:38
1K0
发布2019-07-01 13:12:38
举报
文章被收录于专栏:dylanliudylanliu
  • 先贴一下老版本(3.0.4.RELEASE)配置的SpringMVC,由于项目之前就有Spring的配置,在此只贴与SpringMVC相关的

web.xml

代码语言:javascript
复制
    <listener>
        <listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>
    </listener>

    <servlet>
        <servlet-name>dispatcherServlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:spring/applicationContext-springmvc.xml</param-value>
        </init-param>
    </servlet>
    <servlet-mapping>
        <servlet-name>dispatcherServlet</servlet-name>
        <url-pattern>*.htm</url-pattern>
    </servlet-mapping>

maven spring相关包:

代码语言:javascript
复制
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
        </dependency>
        <dependency>
            <groupId>javax.servlet.jsp</groupId>
            <artifactId>jsp-api</artifactId>
            <version>2.0</version>
            <scope>provided</scope>
        </dependency>

maven Json相关包:

代码语言:javascript
复制
             <dependency>
                <groupId>org.codehaus.jackson</groupId>
                <artifactId>jackson-core-asl</artifactId>
                <version>1.9.9</version>
            </dependency>
            <dependency>
                <groupId>org.codehaus.jackson</groupId>
                <artifactId>jackson-mapper-asl</artifactId>
                <version>1.9.9</version>
            </dependency>

springmvc相关配置:

代码语言:javascript
复制
    <description>Springmvc配置</description>
    <context:component-scan base-package="com"/>

    <mvc:annotation-driven />
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" p:prefix="/WEB-INF/content/" p:suffix=".jsp" />
  • 升级到新版本(4.1.2.RELEASE)之后将配置直接拿过来遇到的问题
  1. @ResponseBody返回String没有问题,返回POJO与Map等页面报406错误
  2. ApplicationAware类报NPE错误
  3. 其他与具体项目相关的错误

尝试过的解决办法:

2、3是首先解决的,因为是具体项目相关的东西,在此不多说,主要说下1

1:Google了一下Tomcat 406问题,Stackoverflow(问题地址 有很多个,大家可以自行搜索,这里只贴一个)上说缺少jar包,于是将以下jar包加入到pom中,结果:没用

代码语言:javascript
复制
            <dependency>
                <groupId>com.fasterxml.jackson.core</groupId>
                <artifactId>jackson-core</artifactId>
                <version>2.5.0</version>
            </dependency>
            <dependency>
                <groupId>com.fasterxml.jackson.core</groupId>
                <artifactId>jackson-annotations</artifactId>
                <version>2.5.0</version>
            </dependency>
            <dependency>
                <groupId>com.fasterxml.jackson.core</groupId>
                <artifactId>jackson-databind</artifactId>
                <version>2.5.0</version>
            </dependency>

2:再次找解决方案,看到了这个MessageConverter,Stackoverflow地址博客园地址,于是将以下配置加入到SpringMVC xml中

代码语言:javascript
复制
    <!-- Json转换器配置 -->
    <bean id="mappingJackson2HttpMessageConverter"
          class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
        <property name="supportedMediaTypes">
            <list>
                <value>text/html;charset=UTF-8</value>
            </list>
        </property>
    </bean>
    <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
        <property name="messageConverters">
            <list>
                <ref bean="mappingJackson2HttpMessageConverter" />
            </list>
        </property>
    </bean>

注:此处将AnnotationMethodHandlerAdapter(Deprecated) 替换成了RequestMappingHandlerAdapter

结果:还是报406错误

3:自己琢磨可能是因为没有application/json 配置导致的?,于是在supportedMediaTypes里面加入了

<value>application/json;charset=UTF-8</value>

结果:还是不行

4: 其实做了上面几步已经把外部相关的资源都看过了,但是对问题的解决并没有帮助,于是只能debug代码,看spring对相应的类型是如何做转换的了

在controller方法返回后,spring会对请求与可提供的类型转换做匹配,代码如下:

代码语言:javascript
复制
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);
		}
	}

getProducibleMediaTypes 这个方法是获得可用的转换类:

代码语言:javascript
复制
protected List<MediaType> getProducibleMediaTypes(HttpServletRequest request, Class<?> returnValueClass) {
		Set<MediaType> mediaTypes = (Set<MediaType>) request.getAttribute(HandlerMapping.PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE);
		if (!CollectionUtils.isEmpty(mediaTypes)) {
			return new ArrayList<MediaType>(mediaTypes);
		}
		else if (!this.allSupportedMediaTypes.isEmpty()) {
			List<MediaType> result = new ArrayList<MediaType>();
			for (HttpMessageConverter<?> converter : this.messageConverters) {
				if (converter.canWrite(returnValueClass, null)) {
					result.addAll(converter.getSupportedMediaTypes());
				}
			}
			return result;
		}
		else {
			return Collections.singletonList(MediaType.ALL);
		}
	}

到这的时候可以发现this.messageConverters里面全都是spring默认的类型转换处理类配置,并没有自己在springmvc xml中的converter配置,于是继续在setter上打断点

代码语言:javascript
复制
public void setSupportedMediaTypes(List<MediaType> supportedMediaTypes) {
        Assert.notEmpty(supportedMediaTypes, "\'supportedMediaTypes\' must not be empty");
        this.supportedMediaTypes = new ArrayList(supportedMediaTypes);
    }

这个方法调用了n次,终于在某一个闪过的supportMediaTypes中看到了自己配置的text/html;charset=UTF-8的MappingJackson2HttpMessageConverter,于是启动完毕继续测试。

结果:还是406

5:上面的调试走了几遍,突然想到是不是Converter的配置被Spring的默认配置给覆盖了?因为<mvc:annotation-driven/> 是会自动配置一些Converter还有其他的东西的,但是以前自己曾经的项目中貌似没有问题,抱着不能错过的态度搜了一下<mvc:annotation-driven/>中加载的类及类的优先级关系,看到了这篇文章,里面写到了mvc的annotation-driven配置的类优先级为0!也就是优先级最高,自己配置的Converter根本不起作用,于是把mvc xml配置改为

代码语言:javascript
复制
    <!-- Json转换器配置 -->
    <bean id="mappingJackson2HttpMessageConverter"
          class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
        <property name="supportedMediaTypes">
            <list>
                <value>text/html;charset=UTF-8</value>
            </list>
        </property>
    </bean>
    <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
        <property name="messageConverters">
            <list>
                <ref bean="mappingJackson2HttpMessageConverter" />
            </list>
        </property>
        <property name="order" value="-1"/>
    </bean>

加入了order为-1的property,再次测试,成功。

找了一下annotation-driven的spring配置类(AnnotationDrivenBeanDefinitionParser),可以看到如下代码:

代码语言:javascript
复制
RootBeanDefinition handlerMappingDef = new RootBeanDefinition(RequestMappingHandlerMapping.class);
handlerMappingDef.setSource(source);
handlerMappingDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
handlerMappingDef.getPropertyValues().add("order", 0);
handlerMappingDef.getPropertyValues().add("contentNegotiationManager", contentNegotiationManager);
String methodMappingName = parserContext.getReaderContext().registerWithGeneratedName(handlerMappingDef);

注意这行

代码语言:javascript
复制
handlerMappingDef.getPropertyValues().add("order", 0);

至此问题解决。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

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

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

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

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档