Content type 'application/x-www-form-urlencoded;charset=UTF-8' not supported

news/2024/7/6 23:24:34

记一次Java Bug的解决过程

Q:Bug描述

前端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: 第二种解决办法

自定义参数解析器:

  1. 首先定义注解
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER})
public @interface P {
    // ...
}
复制代码
  1. 自定义参数解析器的实现
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
	{
        // 返回对应的参数类型的数据    
	}
}
复制代码
  1. 配置参数解析器
@Configuration
public class ArgumentResolversConfig implements WebMvcConfigurer
{
    @Override
	public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers)
	{
		resolvers.add(new Form2PojoArgumentResolver());
	}
}
复制代码
  1. 相应的控制器的方法中
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就聊到这儿吧。


http://www.niftyadmin.cn/n/2747038.html

相关文章

【持续更新】NOIP注意事项

1.无根据的乱搞不可能对 2.必须造极限数据跑一下 3.必须测空间 4.不管用不用都把cstring加上 5.开文件测样例 6.删一长串代码最好注释 7.到10:00先敲暴力 8.题读三遍 9.先做好得分的&#xff0c;而不是先做好玩的 10.不准写LCT之类的神奇东西 11.就算存不下也要把dp方程写出来 …

02、在层级未知情况下通过递归查找子物体

1、在在层级未知情况下通过递归查找子物体 &#xff0c;这个主要是用于UI的的层级查找中 2、代码&#xff1a; 1 using System.Collections;2 using System.Collections.Generic;3 using UnityEngine;4 5 public class EnemyManager : MonoBehaviour6 {7 8 private GameOb…

读卡购票c语言程序,基于51单片机的c语言韦根卡读卡程序 门禁系统

/******************************************************************************** 文件名称&#xff1a;Wiegand.c* 说明&#xff1a;本文件为韦根卡读卡程序。* 功能&#xff1a;实现对韦根卡的识别* 修改&#xff1a;无* 版本&#xff1a;1.0.0* 作者&#xff1a;YuanDo…

LeetCode--100--相同的树

问题描述&#xff1a; 给定两个二叉树&#xff0c;编写一个函数来检验它们是否相同。 如果两个树在结构上相同&#xff0c;并且节点具有相同的值&#xff0c;则认为它们是相同的。 示例 1: 输入: 1 1/ \ / \2 3 2 3[1,2,3], [1,2,3]输出: true 示…

甘肃电大c语言考试题答案,2017年电大C语言程序设计期末考试复习题及答案.doc...

C语言考试期末考试复习题及答案选择题一个C语言程序总是从( )开始执行书写顺序的第一个函数书写顺序的第一条执行语句主函数main( )不确定设int x3&#xff0c;y4&#xff0c;z5&#xff0c;则下列表达式中的值为0的是 ( )A) ‘x’&&’y’B) x||yz&&y-z C) x&l…

关于闭包即双层装饰器的理解

** 闭包**&#xff1a;就是一个概念&#xff0c;出现在嵌套函数中&#xff0c;指的是内层函数引用到了外层函数的**自由变量**(未在本地作用域中定义的局部变量)&#xff0c;就形成了闭包。函数的局部作用域是不能够保存信息的&#xff0c;即在函数内部声明变量在函数调用结束之…

(转) 网站统计中的数据收集原理及实现

原文地址&#xff1a;http://blog.codinglabs.org/articles/how-web-analytics-data-collection-system-work.html 网站数据统计分析工具是网站站长和运营人员经常使用的一种工具&#xff0c;比较常用的有谷歌分析、百度统计和腾讯分析等等。所有这些统计分析工具的第一步都是网…

c语言程序设计 对单词排序,C语言课程设计英语词典排版系统.doc

C语言课程设计院系&#xff1a;姓名&#xff1a;学号&#xff1a;班号&#xff1a;指导教师&#xff1a;日期&#xff1a;2010年9月第一部分1.设计题目&#xff1a;一种简单份的英文词典排版系统2实践目的通过进行计算机实践&#xff0c;更加系统地理解和掌握C语言的基本概念、…