springboot @requestbody的编码问题_@requestbody 字符集-程序员宅基地

技术标签: ------springboot  字符编码  springboot  

最近遇到一个很蛋疼的问题,机器发来http请求,信息都放在body Data里用gb2312编码,然后后台用@RequestBody来接受,这时问题来了,机器发来的请求没有设置content-type,于是默认就是content-type:application/x-www-form-urlencoded,然后spring容器就默认设置CharacterEncoding为utf-8,来解码。更奇怪的是,spring中途还对body使用urlencode。

比方说http body内容是“温度设定值”,最后接受到的是

%ef%bf%bd%c2%b6%ef%bf%bd%ef%bf%bd%e8%b6%a8%d6%b5,用URLDecode.decode(data,"gb2312")解码后是“锟铰讹拷锟借定值”,经过我多次试验,实际过程是这样的,

	public static void main(String[] args) {
		try {
			String data = "温度设定值";
			System.out.println(data);
			data = new String(data.getBytes("gb2312"),"utf-8");
			System.out.println(data);
			data = URLEncoder.encode(data,"utf-8");
			System.out.println(data);
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
温度设定值
�¶��趨ֵ
%EF%BF%BD%C2%B6%EF%BF%BD%EF%BF%BD%E8%B6%A8%D6%B5

多次debug后终于发现了spring自作主张对内容进行urlencode,在ServletServerHttpRequest类的

getBodyFromServletRequestParameters方法中

	/**
	 * Use {@link javax.servlet.ServletRequest#getParameterMap()} to reconstruct the
	 * body of a form 'POST' providing a predictable outcome as opposed to reading
	 * from the body, which can fail if any other code has used the ServletRequest
	 * to access a parameter, thus causing the input stream to be "consumed".
	 */
	private static InputStream getBodyFromServletRequestParameters(HttpServletRequest request) throws IOException {
		ByteArrayOutputStream bos = new ByteArrayOutputStream(1024);
		Writer writer = new OutputStreamWriter(bos, FORM_CHARSET);

		Map<String, String[]> form = request.getParameterMap();
		for (Iterator<String> nameIterator = form.keySet().iterator(); nameIterator.hasNext();) {
			String name = nameIterator.next();
			List<String> values = Arrays.asList(form.get(name));
			for (Iterator<String> valueIterator = values.iterator(); valueIterator.hasNext();) {
				String value = valueIterator.next();
				writer.write(URLEncoder.encode(name, FORM_CHARSET));
				if (value != null) {
					writer.write('=');
					writer.write(URLEncoder.encode(value, FORM_CHARSET));
					if (valueIterator.hasNext()) {
						writer.write('&');
					}
				}
			}
			if (nameIterator.hasNext()) {
				writer.append('&');
			}
		}
		writer.flush();

		return new ByteArrayInputStream(bos.toByteArray());
	}
	protected static final String FORM_CHARSET = "UTF-8";

FORM_CHARSET是静态常量,也就是说是固定的。spring为什么要将@RequestBody接受的数据进行urlencode编码我不得而知,总之我们知道了不管怎样最后都应该用UrlDecode.decode(data,"utf-8")进行解码,然后问题就变成了如何解决spring用utf-8默认解码的问题。

 

查了很多资料,也试验了很多,最快捷的方法自然是机器传来的信息加上消息头 content-type charset=GB2312,这样tomcat容器就会使用gb2312进行解码了。但是我的提议没有被接受。那么只有强制在这个消息路径里使用

request.setCharacterEncoding("gbk2312")了,但是servlet规定只有在调用request.getParameters()之前设置

characterEncoding才会生效,而spring容器早就不知道在之前做过了多少事情了。我试图在过滤器中设置request编码,但很可惜并不生效,说明spring在过滤器之前就调用了getParameters方法。

@WebFilter(filterName = "encodingFilter", urlPatterns = "/*")
public class MutiCharacterEncodingFilter extends OncePerRequestFilter

在这个问题上我花费了大量时间,几乎绝望了,网上的信息都是说要在getParameters之前设置request编码,这我已经充分了解了,但是你告诉我加在哪儿啊?怎么在spring做出处理之前设置编码。最绝望的就是,你知道解决问题的方法,但却不知道怎么实现。最后我幸运地找到了资料,很可惜并不是我自己独立完成的。

 

Spring boot 字符集编码

作者跟踪源码,发现CharacterEncodingFilter会调用request.setCharacterEncoding("UTF-8"),于是他写了一个类继承

CharacterEncodingFilter,并在Application中注入它

    @Bean()
    @ConfigurationProperties(prefix = "spring.http.encoding")
    @ConfigurationPropertiesBinding
    public MutiCharacterEncodingFilter mutiCharacterEncodingFilter(){
        MutiCharacterEncodingFilter encodingFilter = new MutiCharacterEncodingFilter();
        encodingFilter.setEncoding(charset);
        encodingFilter.setForceRequestEncoding(forceRequest);
        encodingFilter.setForceResponseEncoding(forceResponse);
        return encodingFilter;
    }

这样自定义的过滤器就会取代CharacterEncodingFilter。request.setCharacterEncoding也就生效了。

这让我对springboot的了解更加得深入,之前都是copy别人的代码,发现有很多用@Bean的方式配置变量,一直只知其然不知其所以然。看来springboot会把@Bean标注的变量替换掉他默认的变量,只要这个变量继承了那个默认变量。而我用

@WebFilter加入的过滤器只是加在这些默认过滤器的后面,而不是替换这些默认过滤器。

至此,乱码问题终于解决

 

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/qq_36804701/article/details/81011397

智能推荐

MM自动过账-库存科目与抵消类科目GBB配置_sap gbb-程序员宅基地

文章浏览阅读8.3k次,点赞8次,收藏56次。一. 自动过账原理 在MM模块的许多操作都能实现在FI模块自动过账,如PO收货、发票验证(LIV)、工单发料、向生产车间发料等等。不用说,一定需要在IMG中进行配置才可以实现自动处理。但SAP实现的这种自动配置的机制是怎样的呢?其实也并不复杂,让我们先以一种最简单的情况来了解实现原理和实现流程,然后就可以轻松对各种情况作出配置。 如果我们使用SAP系统,初始化库存一定必不可少。大家都知_sap gbb

没有画的画册-程序员宅基地

文章浏览阅读537次。前 记  说起来也真奇怪!当我感觉得最温暖和最愉快的时候,我的双手和舌头就好像有了束缚,使我不能表达和说出我内心所起的思想。然而我却是一个画家呢。我的眼睛这样告诉我;看到过我的速写和画的人也都这样承认。  我是一个穷苦的孩子。我的住..._天才的荣誉中会被埋入尘土只有平庸的材料获得称赞这是一个古老的故事不过这故

安卓ROM开发入门到精通-程序员宅基地

文章浏览阅读1w次,点赞10次,收藏44次。包含内容:第一期主要为ROM开发,ROM定制技术资料,提供一些工具为主 ,让你学会简单做包,和基本的ROM DIY技巧,偶尔附加一些必要的教程。第二期针对ROM技术教程,细分知识点,重点剖析、解释,形成一系列的知识文档供查阅,目前大纲已列出,可以教你从零开始做包。1.第一期---技术资料(维护更新):更新日志:1.书籍-Android系统级深入开发——移植与调试2.开发工具-And..._安卓rom开发

SCIP | 数学规划求解器SCIP超详细的使用教程_混合整数规划求解器——scip-程序员宅基地

文章浏览阅读1w次,点赞4次,收藏33次。前言小伙伴们大家好呀!继上次lp_solve规划求解器的推文出来以后,大家都期待着更多求解器的具体介绍和用法。小编哪敢偷懒,这不,赶在考试周之际,又在忙里偷闲中给大家送上一篇SCIP规划求解的推文教程。快一起来看看吧。Part1 惯例科普篇What is SCIP?官方的介绍:SCIP is currently one of the fastest non-commerci..._混合整数规划求解器——scip

怎么拍照翻译外语?几款工具让你轻松get韩语-程序员宅基地

文章浏览阅读1.2k次,点赞48次,收藏12次。别担心,现在有了韩语拍照翻译的软件,一切都变得简单啦!,使得在没有网络连接的情况下也能使用翻译服务,这对于经常出国或在网络不稳定区域的用户来说是一个巨大的优势。只需打开应用,选择拍照翻译,对准需要翻译的文字,即可获得翻译结果。你还不知道有哪些好用的拍照翻译工具?它提供了图片翻译功能,可以在编辑文档时直接翻译图片中的文字。,只需选择拍照翻译,对准文本即可获得翻译结果,非常适合需要快速翻译的场合。它的图片翻译功能界面直观,可以轻松上传图片进行翻译。,它都能迅速得到翻译结果,极大地方便了多语言环境下的沟通。

【逗老师带你学IT】Kiwi Syslog Web Access与Active Directory集成认证-程序员宅基地

文章浏览阅读4.1k次。Kiwi是windows平台下一个不错的syslog软件。_kiwi syslog web access

随便推点

undefined reference to ‘cv::imread问题调查_undefined reference to `cv::imread-程序员宅基地

文章浏览阅读4.7k次,点赞2次,收藏2次。Android ndk 编译报错:error: undefined reference to 'cv::imread(std::__ndk1::basic_string<char, std::__ndk1::char_traits<char>, std::__ndk1::allocator<char> > const&, int)' #include <opencv2/core.hpp> #include <openc_undefined reference to `cv::imread

Vite 的好与坏,你怎么看?-程序员宅基地

文章浏览阅读7.6k次,点赞4次,收藏2次。全文 3000 字,欢迎点赞关注转发一、Vite 是什么2020年4月,尤大大发了这么一个推:随后,2021年2月,Vite 2.0 它来了,上来就是一套组合拳:基于 esbuild 实现..._怎么评价vite

python基础之运算符(五)_python使用赋值运算符将num1增加5-程序员宅基地

文章浏览阅读245次。文章目录- 算术运算符- 赋值运算符- 复合赋值运算符- 位运算符- 比较运算符- 逻辑运算符- 成员运算符- 身份运算符- 算符优先级运算符python支持以下几种运算符- 算术运算符下面以a=10 ,b=20为例进行计算运算符描述实例+加两个对象相加 a + b 输出结果 30-减得到负数或是一个数减去另一个数 a - b 输出结果 -10*..._python使用赋值运算符将num1增加5

启动计算机引导windows10,Win10系统引导项丢失了怎么办?修复Win10系统启动引导项的方法...-程序员宅基地

文章浏览阅读8k次,点赞2次,收藏10次。近来,有位用户反馈自己在扩大电脑C盘容量后,发现D盘win10系统的引导文件突然丢失了。这是怎么回事呢?原来该用户的电脑C盘中原先装有Win7系统,D盘中则安装了Win10系统,经过无损分区后,就导致了D盘Win10开机系统启动选项的丢失。下面,小编就向大家分享Win10系统引导项丢失问题的解决方法。解决方法:1、如果不想重装Win10的话,只能用EasyBCD来修复系统启动引导项了。先下载Eas..._修复电脑启动引导项

Word报告自动生成(例如 导出数据库结构),理论+实战双管齐下_一键生成word报告-程序员宅基地

文章浏览阅读750次,点赞23次,收藏12次。以图四为例,数据库有多少张表是不固定的,我们在制作模板的时候不可能先画好N(N为表的总数)个表格等待数据填充, 这里就会需要遍历数据源中提供的所有表结构数据,然后逐一形成表格。注意这里省略了表序号,当级别为0的时候 ,自动取最后一个datatable中的数据,因为这个label经常会用到其他表汇总的数据,可能会用到之前几张表的数据,所以放在其他表都处理好后。当级别为1的时候,自然取该级别循环的行数据。级别:默认文档级别为0,出现的第一层循环为1,其内部若再次嵌套循环则级别为2,依次类推。_一键生成word报告

人脸识别(一)调用face++实现人脸检测。_face px-程序员宅基地

文章浏览阅读2w次,点赞3次,收藏37次。2017年下半年以来,随着iPhoneX的人脸解锁功能把人脸识别这一黑科技带入大家的视野中之后,各种有关人脸识别功能的新闻和报道层出不穷。不仅是对普通群众来说,对我们程序猿来说,百度,微软,阿里等各大公司推出的可供调用的人脸识别api也如雨后春笋一般冒出来。鉴于公司以后业务发展需要,同时也是个人兴趣所致,对调用其他公司api实现人脸识别进行了一定的技术调研,于是调研成果写成几篇博客分享出来,供大家一_face px

推荐文章

热门文章

相关标签