前端form表单数据提交时,后端出现 Resolved [org.springframework.web.HttpMediaTypeNotSupportedException: Content type 'application/x-www-form-urlencoded;charset=UTF-8' not supported]
这样的提示,也没有触发Controller的响应方法。
A: 第一种解决办法
ajax请求的时候,把Content-Type,设置为 application/json; charset=utf-8
A: 第二种解决办法
- 首先定义注解
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER})
public @interface P {
// ...
}
复制代码
- 自定义参数解析器的实现
public class Form2PojoArgumentResolver implements HandlerMethodArgumentResolver
{
@Override
public boolean supportsParameter(MethodParameter parameter)
{
return parameter.hasParameterAnnotation(P.class);
}
@Nullable
@Override
public Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer, NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception
{
// 返回对应的参数类型的数据
}
}
复制代码
- 配置参数解析器
@Configuration public class ArgumentResolversConfig implements WebMvcConfigurer { @Override public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) { resolvers.add(new Form2PojoArgumentResolver()); } } 复制代码
- 相应的控制器的方法中
public class TestController
{
@RequestMapping("xxx")
public testArgumentResolver(@P TestEntity entity)
{
// ...
}
}
复制代码
通常情况下,前端form表单的数据到Controller中时就转换成的对象。但是我这里出现了特殊情形。 由于我的TestEntity实现了Map接口,而Spring Boot在启动时会自动加载一系列的解析器,而这些解析其中有一个叫 MapMethodProcessor
的处理器,它的 supportsParameter
方法是这样实现的:
@Override public boolean supportsParameter(MethodParameter parameter) { return Map.class.isAssignableFrom(parameter.getParameterType()); } 复制代码
这就导致了,在调用获取方法参数的方法时(即调用 InvocableHandlerMethod.java
中的 getMethodArgumentValues
时)直接使用了 MapMethodProcessor
参数解析器(处理器),而自定义的参数解析器并没有被调用。
翻了一下 HandlerMethodArgumentResolverComposite.java
的源码,参数解析器的选取是顺序遍历的,源码如下:
/** * Find a registered {@link HandlerMethodArgumentResolver} that supports the given method parameter. */ @Nullable private HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parameter) { HandlerMethodArgumentResolver result = this.argumentResolverCache.get(parameter); if (result == null) { // 遍历参数解析器,寻找符合要求的 for (HandlerMethodArgumentResolver methodArgumentResolver : this.argumentResolvers) { if (logger.isTraceEnabled()) { logger.trace("Testing if argument resolver [" + methodArgumentResolver + "] supports [" + parameter.getGenericParameterType() + "]"); } if (methodArgumentResolver.supportsParameter(parameter)) { result = methodArgumentResolver; this.argumentResolverCache.put(parameter, result); break; } } } return result; } 复制代码
又翻了一下 RequestMappingHandlerAdapter.java
的源码,参数解析器的加载顺序是这个样子的:
/** * Return the list of argument resolvers to use including built-in resolvers * and custom resolvers provided via {@link #setCustomArgumentResolvers}. */ private List<HandlerMethodArgumentResolver> getDefaultArgumentResolvers() { List<HandlerMethodArgumentResolver> resolvers = new ArrayList<>(); // Annotation-based argument resolution resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), false)); resolvers.add(new RequestParamMapMethodArgumentResolver()); resolvers.add(new PathVariableMethodArgumentResolver()); resolvers.add(new PathVariableMapMethodArgumentResolver()); resolvers.add(new MatrixVariableMethodArgumentResolver()); resolvers.add(new MatrixVariableMapMethodArgumentResolver()); resolvers.add(new ServletModelAttributeMethodProcessor(false)); resolvers.add(new RequestResponseBodyMethodProcessor(getMessageConverters(), this.requestResponseBodyAdvice)); resolvers.add(new RequestPartMethodArgumentResolver(getMessageConverters(), this.requestResponseBodyAdvice)); resolvers.add(new RequestHeaderMethodArgumentResolver(getBeanFactory())); resolvers.add(new RequestHeaderMapMethodArgumentResolver()); resolvers.add(new ServletCookieValueMethodArgumentResolver(getBeanFactory())); resolvers.add(new ExpressionValueMethodArgumentResolver(getBeanFactory())); resolvers.add(new SessionAttributeMethodArgumentResolver()); resolvers.add(new RequestAttributeMethodArgumentResolver()); // Type-based argument resolution resolvers.add(new ServletRequestMethodArgumentResolver()); resolvers.add(new ServletResponseMethodArgumentResolver()); resolvers.add(new HttpEntityMethodProcessor(getMessageConverters(), this.requestResponseBodyAdvice)); resolvers.add(new RedirectAttributesMethodArgumentResolver()); resolvers.add(new ModelMethodProcessor()); // 卧槽,你在这儿啊 resolvers.add(new MapMethodProcessor()); resolvers.add(new ErrorsMethodArgumentResolver()); resolvers.add(new SessionStatusMethodArgumentResolver()); resolvers.add(new UriComponentsBuilderMethodArgumentResolver()); // Custom arguments // 妈蛋,我在这儿 if (getCustomArgumentResolvers() != null) { resolvers.addAll(getCustomArgumentResolvers()); } // Catch-all resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), true)); resolvers.add(new ServletModelAttributeMethodProcessor(true)); return resolvers; } 复制代码
好吧,怪不得轮不到自定义的参数解析器来处理。既然如此,那我改变一下自定义参数解析器的加载顺序吧————渣渣我要骑到你们头上去!
@Configuration
public class ArgumentResolversConfig
{
@Autowired
private RequestMappingHandlerAdapter adapter;
@PostConstruct
public void injectSelfMethodArgumentResolver()
{
List<HandlerMethodArgumentResolver> argumentResolvers = new ArrayList<>();
argumentResolvers.add(new Form2PojoArgumentResolver());
argumentResolvers.addAll(adapter.getArgumentResolvers());
adapter.setArgumentResolvers(argumentResolvers);
}
}
复制代码
好了,如此,完美的解决了上述问题。
这个Bug就聊到这儿吧。

原文
https://juejin.im/post/5be3f12fe51d4566f5205eb9
本站部分文章源于互联网,本着传播知识、有益学习和研究的目的进行的转载,为网友免费提供。如有著作权人或出版方提出异议,本站将立即删除。如果您对文章转载有任何疑问请告之我们,以便我们及时纠正。PS:推荐一个微信公众号: askHarries 或者qq群:474807195,里面会分享一些资深架构师录制的视频录像:有Spring,MyBatis,Netty源码分析,高并发、高性能、分布式、微服务架构的原理,JVM性能优化这些成为架构师必备的知识体系。还能领取免费的学习资源,目前受益良多

转载请注明原文出处:Harries Blog™ » Content type 'application/x-www-form-urlencoded;charset=UTF-8' not supported