springboot @requestbody的编码问题

最近遇到一个很蛋疼的问题,机器发来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加入的过滤器只是加在这些默认过滤器的后面,而不是替换这些默认过滤器。

至此,乱码问题终于解决

 

  • 5
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 21
    评论
回答: 当使用@RequestBody注解时,如果请求的JSON串中的属性名与bean中的属性名不一致,可以通过给每个属性指定别名的方式解决。可以在bean中使用@JsonProperty注解来指定属性的别名,例如:\[1\]。这样就可以将客户端传过来的变量赋值到相应的bean中了。 另外,需要注意的是,@RequestBody一般用来处理非Content-Type: application/x-www-form-urlencoded编码格式的数据。在GET请求中,由于没有HttpEntity,所以@RequestBody并不适用。而在POST请求中,通过HttpEntity传递的参数,需要在请求头中声明数据的类型Content-Type。SpringMVC会使用HandlerAdapter配置的HttpMessageConverters来解析HttpEntity中的数据,并将其绑定到相应的bean上。\[2\] 此外,如果你在使用Spring Boot时遇到了JSON属性首字母大小写的问题,可以考虑使用fastjson作为JSON解析框架。Spring Boot默认使用的是Jackson,对于从.NET转Java的项目来说,首字母大写的属性会自动转为小写,这可能导致前端出现问题。使用fastjson可以解决这个问题,可以参考这篇文章来了解如何在Spring Boot中使用fastjson:\[3\]。 #### 引用[.reference_title] - *1* [spring中@RequestBody,bean中属性名和json不一致解决方案](https://blog.csdn.net/yueguanyun/article/details/80265381)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [SpringMVC @RequestBody 参数映射不上,@RequestBody 报错](https://blog.csdn.net/liuzhenteng521/article/details/82592245)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] - *3* [springboot @RequestBody bean 对象 为空问题](https://blog.csdn.net/weixin_43531940/article/details/109015203)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 21
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值