springboot中@ReqquestBody注解的使用以及不生效的原因

一:背景

自己有两个项目,一个项目中使用@RequestBody注解是有效的,而另一个项目中无效,一直报异常:

2021-04-07 18:56:48.483  WARN 11484 --- [io-8288-exec-10] .[org.springframework.web.HttpMediaTypeNotSupportedException:
 Content type 'application/x-www-form-urlencoded;charset=UTF-8' not supported]

网上找到的解决办法大都是说依赖未添加正确,还有其他一些办法都没有有效的解决问题。无果之后自己从源码入手,找到了出问题的原因。

二:如何使用@RequestBody注解

1.前端传递参数时需设置数据类型为applicaition/json格式,如果使用的是axios,代码如下所示

axios({
	method: 'post',
	headers: {
		'Content-Type': 'application/json'
	},
	url: 'http://localhost:8288/test',
	data: {name: 'zhangsan',age:10},
}).then(res => {});

2.后端使用@RequestBody注解接收,代码如下所示:

@RequestMapping("/test")
public String test(@RequestBody User user){
    
    return "success";
}

3.springboot默认添加了jackson依赖,不需要我们进行额外的引入,网上很多解决办法说需要引入该依赖。。

三: @RequestBody注解无效、后台报异常的原因

项目中出现@RequestBody注解无效的情况后,首先检查了前端是否设置了正确的'Content-Type': 'application/json',然后检查了后端是否使用正确的方式接收。经确定,两者都是正确的。经过百度问题无法解决后,从源码入手找到了问题的原因:自己配置文件中的一段代码覆盖了springboot默认加载的配置。如下图代码所示:

public class MyWebConfig extends WebMvcConfigurationSupport {
	@Bean
	public HttpMessageConverter responseBodyConverter(){
	    //解决返回值中文乱码
	    StringHttpMessageConverter converter = new StringHttpMessageConverter(Charset.forName("UTF-8"));
	    return converter;
	}
	
	@Override
	public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
	    converters.add(responseBodyConverter());
	}
}

上述代码是在网上百度到的一段解决请求返回中文乱码的代码。在看了官方的注释文档之后发现该代码会springboot默认加载的默认转换器。原文如下:

/**
	 * Override this method to add custom {@link HttpMessageConverter HttpMessageConverters}
	 * to use with the {@link RequestMappingHandlerAdapter} and the
	 * {@link ExceptionHandlerExceptionResolver}.
	 * <p>Adding converters to the list turns off the default converters that would
	 * otherwise be registered by default. Also see {@link #addDefaultHttpMessageConverters}
	 * for adding default message converters.
	 * @param converters a list to add message converters to (initially an empty list)
*/

这段话的大致意思就是覆盖configureMessageConverters这个方法会导致springboot默认的converters(数据转换器)关闭,只保留我在代码中添加的StringHttpMessageConverter这个转换器。
很明显可以看出,它只支持String类型转换,不支持json,这就是问题出现的原因。那么如何解决该问题呢:经查看源码,还有一个方法可以将默认的转换器添加进去,代码如下:

@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
    converters.add(responseBodyConverter());
    //加载默认的数据转换器至转换器列表中
    super.addDefaultHttpMessageConverters(converters);
}

我们查看addDefaultHttpMessageConverters这个方法的内容:

protected final void addDefaultHttpMessageConverters(List<HttpMessageConverter<?>> messageConverters) {
		messageConverters.add(new ByteArrayHttpMessageConverter());
		messageConverters.add(new StringHttpMessageConverter());
		messageConverters.add(new ResourceHttpMessageConverter());
		messageConverters.add(new ResourceRegionHttpMessageConverter());
		if (!shouldIgnoreXml) {
			try {
				messageConverters.add(new SourceHttpMessageConverter<>());
			}
			catch (Throwable ex) {
				// Ignore when no TransformerFactory implementation is available...
			}
		}
		messageConverters.add(new AllEncompassingFormHttpMessageConverter());

		if (romePresent) {
			messageConverters.add(new AtomFeedHttpMessageConverter());
			messageConverters.add(new RssChannelHttpMessageConverter());
		}

		if (!shouldIgnoreXml) {
			if (jackson2XmlPresent) {
				Jackson2ObjectMapperBuilder builder = Jackson2ObjectMapperBuilder.xml();
				if (this.applicationContext != null) {
					builder.applicationContext(this.applicationContext);
				}
				messageConverters.add(new MappingJackson2XmlHttpMessageConverter(builder.build()));
			}
			else if (jaxb2Present) {
				messageConverters.add(new Jaxb2RootElementHttpMessageConverter());
			}
		}

		if (kotlinSerializationJsonPresent) {
			messageConverters.add(new KotlinSerializationJsonHttpMessageConverter());
		}
		if (jackson2Present) {
			Jackson2ObjectMapperBuilder builder = Jackson2ObjectMapperBuilder.json();
			if (this.applicationContext != null) {
				builder.applicationContext(this.applicationContext);
			}
			messageConverters.add(new MappingJackson2HttpMessageConverter(builder.build()));
		}
		else if (gsonPresent) {
			messageConverters.add(new GsonHttpMessageConverter());
		}
		else if (jsonbPresent) {
			messageConverters.add(new JsonbHttpMessageConverter());
		}

		if (jackson2SmilePresent) {
			Jackson2ObjectMapperBuilder builder = Jackson2ObjectMapperBuilder.smile();
			if (this.applicationContext != null) {
				builder.applicationContext(this.applicationContext);
			}
			messageConverters.add(new MappingJackson2SmileHttpMessageConverter(builder.build()));
		}
		if (jackson2CborPresent) {
			Jackson2ObjectMapperBuilder builder = Jackson2ObjectMapperBuilder.cbor();
			if (this.applicationContext != null) {
				builder.applicationContext(this.applicationContext);
			}
			messageConverters.add(new MappingJackson2CborHttpMessageConverter(builder.build()));
		}
}

可以发现,这个方法添加的许多类型转换器,其中的MappingJackson2HttpMessageConverter这个就是我们需要的json转换器,查看这个类可以发现,它的注释文档中有以下一段话:

/**
 * Implementation of {@link org.springframework.http.converter.HttpMessageConverter} that can read and
 * write JSON using <a href="https://github.com/FasterXML/jackson">Jackson 2.x's</a> {@link ObjectMapper}.
 *
 * <p>This converter can be used to bind to typed beans, or untyped {@code HashMap} instances.
 *
 * <p>By default, this converter supports {@code application/json} and {@code application/*+json}
 * with {@code UTF-8} character set. This can be overridden by setting the
 * {@link #setSupportedMediaTypes supportedMediaTypes} property.
 *
 */

大概意思就是它支持json类型的数据在转换。

上一篇:spring-core-3-23 | Spring IoC依赖注入:Spring提供了哪些依赖注入模式和类型呢?


下一篇:SpringBoot:MessageConverter自动配置原理