SpringBoot @RequestBody 报错 ('application/x-www-form-urlencoded;charset=UTF-8' not supported)
第一种:转https://blog.csdn.net/chenfei2341/article/details/83652586
在Spring boot 中使用 @RequestBody 会报错,提示错误 Content type 'application/x-www-form-urlencoded;charset=UTF-8' not supported
,代码如下:
@RequestMapping(value = "/act/service/model/{modelId}/save", method = RequestMethod.POST) public void saveModel(@PathVariable String modelId, @RequestBody MultiValueMap<String, String> values) { // 具体代码 }
这个在传统 spring MVC 中是有效的,但是在 Spring boot 中会报错。
传统是 Spring MVC 有效,是因为有 <mvc:annotation-driven> 注解,查资料,<mvc:annotation-driven> 注解配置了如下的内容
spring 3.1 版本:
<!-- 注解请求映射 --> <bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping"> <property name="interceptors"> <list> <ref bean="logNDCInteceptor"/> <!-- 日志拦截器,这是你自定义的拦截器 --> </list> </property> </bean> <bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter"> <property name="messageConverters"> <list> <ref bean="byteArray_hmc" /> <ref bean="string_hmc" /> <ref bean="resource_hmc" /> <ref bean="source_hmc" /> <ref bean="xmlAwareForm_hmc" /> <ref bean="jaxb2RootElement_hmc" /> <ref bean="jackson_hmc" /> </list> </property> </bean> <bean id="byteArray_hmc" class="org.springframework.http.converter.ByteArrayHttpMessageConverter" /><!-- 处理.. --> <bean id="string_hmc" class="org.springframework.http.converter.StringHttpMessageConverter" /><!-- 处理.. --> <bean id="resource_hmc" class="org.springframework.http.converter.ResourceHttpMessageConverter" /><!-- 处理.. --> <bean id="source_hmc" class="org.springframework.http.converter.xml.SourceHttpMessageConverter" /><!-- 处理.. --> <bean id="xmlAwareForm_hmc" class="org.springframework.http.converter.xml.XmlAwareFormHttpMessageConverter" /><!-- 处理.. --> <bean id="jaxb2RootElement_hmc" class="org.springframework.http.converter.xml.Jaxb2RootElementHttpMessageConverter" /><!-- 处理.. --> <bean id="jackson_hmc" class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter" /><!-- 处理json-->
转载:http://elf8848.iteye.com/blog/875830
这个找到的资料是 3.1 的,但是women可以看到,最后一个配置了 Jackson 的 json 处理程序,在更新的版本中,AnnotationMethodHandlerAdapter 已经废弃,使用的是 RequestMappingHandlerAdapter,看下 RequestMappingHandlerAdapter 的源码。
public RequestMappingHandlerAdapter() { StringHttpMessageConverter stringHttpMessageConverter = new StringHttpMessageConverter(); stringHttpMessageConverter.setWriteAcceptCharset(false); // see SPR-7316 this.messageConverters = new ArrayList<HttpMessageConverter<?>>(4); this.messageConverters.add(new ByteArrayHttpMessageConverter()); this.messageConverters.add(stringHttpMessageConverter); this.messageConverters.add(new SourceHttpMessageConverter<Source>()); this.messageConverters.add(new AllEncompassingFormHttpMessageConverter()); }
这里面没有了 json 的处理过程,我们把它加上
@EnableWebMvc @Configuration public class WebMvcConfig extends WebMvcConfigurerAdapter { @Bean public RequestMappingHandlerAdapter requestMappingHandlerAdapter() { RequestMappingHandlerAdapter adapter = new RequestMappingHandlerAdapter(); List<HttpMessageConverter<?>> converters = adapter.getMessageConverters(); MappingJackson2HttpMessageConverter jsonConverter = new MappingJackson2HttpMessageConverter(); List<MediaType> supportedMediaTypes = new ArrayList<MediaType>(); MediaType textMedia = new MediaType(MediaType.TEXT_PLAIN, Charset.forName("UTF-8")); supportedMediaTypes.add(textMedia); MediaType jsonMedia = new MediaType(MediaType.APPLICATION_JSON, Charset.forName("UTF-8")); supportedMediaTypes.add(jsonMedia);jsonConverter.setSupportedMediaTypes(supportedMediaTypes); converters.add(jsonConverter); adapter.setMessageConverters(converters); return adapter; } }
成功,报错消除,正确获取到了参数
第二种 转自https://blog.csdn.net/justry_deng/article/details/99875548
SpringBoot自定义参数解析器,使被@RequestBody标注的参数能额外接收Content-Type为application/x-www-form-urlencoded的请求
基础知识介绍:
在SpringBoot里,若Controller层里方法的形参前使用了@RequestBody注解,那么该参数将会被RequestResponseBodyMethodProcessor解析器进行解析,若此时Content-Type为application/x-www-form-urlencoded,那么会报Unsupported Media Type错误,这就要求:请求的Content-Type必须为application/json了。
一般的,若Controller层里方法的形参前(不管有没有其它注解,只要)没使用@RequestBody注解,那么该参数几乎都是符合ServletModelAttributeMethodProcessor解析器要求的,进而会使用ServletModelAttributeMethodProcessor解析器进行解析;当请求的Content-Type为application/x-www-form-urlencoded时,几乎用的都是ServletModelAttributeMethodProcessor解析器。
注:参数符合RequestResponseBodyMethodProcessor解析器要求的条件是
parameter.hasParameterAnnotation(RequestBody.class)。可详见
RequestResponseBodyMethodProcessor类源码。
注:参数符合ServletModelAttributeMethodProcessor解析器要求的条件是
parameter.hasParameterAnnotation(ModelAttribute.class) ||
this.annotationNotRequired && !BeanUtils.isSimpleProperty(parameter.getParameterType())。
可详见ModelAttributeMethodProcessor类源码。
需求介绍及实现方式说明:
声明:本文通过实现下述需求,进行示例。
需求介绍:
实现被@RequestBody注解的参数,既能接收Content-Type为application/json的请求的参数值,又能接收Content-Type为application/x-www-form-urlencoded的请求的参数值。
实现方式概述:
自定义参数解析器,当参数前有@RequestBody时,使用该解析器;在该解析器的内部,判断Content-Type,若Content-Type为application/x-www-form-urlencoded,那么采用ServletModelAttributeMethodProcessor解析器;否者采用RequestResponseBodyMethodProcessor解析器。
注:即相当于对RequestResponseBodyMethodProcessor解析器进行了简单的封装。
具体实现代码示例:
自定义参数解析器:
import org.springframework.core.MethodParameter; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.support.WebDataBinderFactory; import org.springframework.web.context.request.NativeWebRequest; import org.springframework.web.method.support.HandlerMethodArgumentResolver; import org.springframework.web.method.support.ModelAndViewContainer; import org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor; import org.springframework.web.servlet.mvc.method.annotation.ServletModelAttributeMethodProcessor; import javax.servlet.http.HttpServletRequest; /** * 自定义参数解析器 * * 注: 自定义参数解析器, 实现对RequestResponseBodyMethodProcessor的扩展 * * 提示: 此解析器要实现的功能是: 若controller方法的参数前, 使用了@RequestBody注解, 那么解析此参数时, * 1、若Content-Type为application/x-www-form-urlencoded, * 那么走ServletModelAttributeMethodProcessor解析器 * 2、若Content-Type不为application/x-www-form-urlencoded, * 那么走本应该走的RequestResponseBodyMethodProcessor解析器 * * @author JustryDeng * @date 2019/8/19 19:47 */ public class MyHandlerMethodArgumentResolver implements HandlerMethodArgumentResolver { /** * 解析Content-Type为application/json的默认解析器是RequestResponseBodyMethodProcessor */ private RequestResponseBodyMethodProcessor requestResponseBodyMethodProcessor; /** * 解析Content-Type为application/x-www-form-urlencoded的默认解析器是ServletModelAttributeMethodProcessor */ private ServletModelAttributeMethodProcessor servletModelAttributeMethodProcessor; /** * 全参构造 */ public MyHandlerMethodArgumentResolver(RequestResponseBodyMethodProcessor requestResponseBodyMethodProcessor, ServletModelAttributeMethodProcessor servletModelAttributeMethodProcessor) { this.requestResponseBodyMethodProcessor = requestResponseBodyMethodProcessor; this.servletModelAttributeMethodProcessor = servletModelAttributeMethodProcessor; } /** * 当参数前有@RequestBody注解时, 解析该参数 会使用此 解析器 * * 注:此方法的返回值将决定:是否使用此解析器解析该参数 */ @Override public boolean supportsParameter(MethodParameter methodParameter) { return methodParameter.hasParameterAnnotation(RequestBody.class); } /** * 解析参数 */ @Override public Object resolveArgument(MethodParameter methodParameter, ModelAndViewContainer modelAndViewContainer, NativeWebRequest nativeWebRequest, WebDataBinderFactory webDataBinderFactory) throws Exception { final String applicationXwwwFormUrlencoded = "application/x-www-form-urlencoded"; HttpServletRequest request = nativeWebRequest.getNativeRequest(HttpServletRequest.class); if (request == null) { throw new RuntimeException(" request must not be null!"); } String contentType = request.getContentType(); /* * 如果ContentType是application/x-www-form-urlencoded,那么使用ServletModelAttributeMethodProcessor解析器 * * 注:其实默认的,当系统识别到参数前有@RequestBody注解时,就会走RequestResponseBodyMethodProcessor解析器;这里就 * 相当于在走默认的解析器前走了个判断而已。 */ if (applicationXwwwFormUrlencoded.equals(contentType)) { return servletModelAttributeMethodProcessor.resolveArgument(methodParameter, modelAndViewContainer, nativeWebRequest, webDataBinderFactory); } return requestResponseBodyMethodProcessor.resolveArgument(methodParameter, modelAndViewContainer, nativeWebRequest, webDataBinderFactory); } }
注册该参数解析器:
import org.springframework.context.annotation.Configuration; import org.springframework.web.method.support.HandlerMethodArgumentResolver; import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter; import org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor; import org.springframework.web.servlet.mvc.method.annotation.ServletModelAttributeMethodProcessor; import javax.annotation.PostConstruct; import java.util.ArrayList; import java.util.List; /** * 配置注册参数解析器 * * @author JustryDeng * @date 2019/8/20 10:13 */ @Configuration public class ConfigArgumentResolvers { private final RequestMappingHandlerAdapter requestMappingHandlerAdapter; public ConfigArgumentResolvers(RequestMappingHandlerAdapter requestMappingHandlerAdapter) { this.requestMappingHandlerAdapter = requestMappingHandlerAdapter; } @PostConstruct private void addArgumentResolvers() { // 获取到的是不可变的集合 List<HandlerMethodArgumentResolver> argumentResolvers = requestMappingHandlerAdapter.getArgumentResolvers(); MyHandlerMethodArgumentResolver myHandlerMethodArgumentResolver = getMyHandlerMethodArgumentResolver(argumentResolvers); // ha.getArgumentResolvers()获取到的是不可变的集合,所以我们需要新建一个集合来放置参数解析器 List<HandlerMethodArgumentResolver> myArgumentResolvers = new ArrayList<>(argumentResolvers.size() + 1); // 将自定义的解析器,放置在第一个; 并保留原来的解析器 myArgumentResolvers.add(myHandlerMethodArgumentResolver); myArgumentResolvers.addAll(argumentResolvers); requestMappingHandlerAdapter.setArgumentResolvers(myArgumentResolvers); } /** * 获取MyHandlerMethodArgumentResolver实例 */ private MyHandlerMethodArgumentResolver getMyHandlerMethodArgumentResolver( List<HandlerMethodArgumentResolver> argumentResolversList) { // 解析Content-Type为application/json的默认解析器 RequestResponseBodyMethodProcessor requestResponseBodyMethodProcessor = null; // 解析Content-Type为application/x-www-form-urlencoded的默认解析器 ServletModelAttributeMethodProcessor servletModelAttributeMethodProcessor = null; if (argumentResolversList == null) { throw new RuntimeException("argumentResolverList must not be null!"); } for (HandlerMethodArgumentResolver argumentResolver : argumentResolversList) { if (requestResponseBodyMethodProcessor != null && servletModelAttributeMethodProcessor != null) { break; } if (argumentResolver instanceof RequestResponseBodyMethodProcessor) { requestResponseBodyMethodProcessor = (RequestResponseBodyMethodProcessor)argumentResolver; continue; } if (argumentResolver instanceof ServletModelAttributeMethodProcessor) { servletModelAttributeMethodProcessor = (ServletModelAttributeMethodProcessor)argumentResolver; } } if (requestResponseBodyMethodProcessor == null || servletModelAttributeMethodProcessor == null) { throw new RuntimeException("requestResponseBodyMethodProcessor and " + " servletModelAttributeMethodProcessor must not be null!"); } return new MyHandlerMethodArgumentResolver(requestResponseBodyMethodProcessor, servletModelAttributeMethodProcessor); }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构