1. 几点说明:
- 使用浏览器发送请求,在地址栏输入url,浏览器都会帮你进行编码;
- 发送http请求的框架,例如HttpClient以及本文要说的zuul,并不会自动编码,需要你对参数进行编码,例如:
URLEncoder.encoder("你的请求参数")
; - springboot框架,controller中获取参数时,会自动进行解码;
2. 问题
媒体项目用到ceph做存储工具,使用AmazonS3来访问ceph对象存储,AmazonS3不支持文件夹/文件的名称以空格开头,所以前端已经将空格字符禁用。但是某天在server端还是出现了空格导致了S3访问失败的问题,controller中打印请求得到:
System.out.println(request.getQueryString());
System.out.println(request.getParameter("pathPrefix"));
pageIndex=0&pageSize=10&userName=develop&bucketName=0002&pathPrefix=+hahaha
hahaha
可以明显看到,pathPrefix在请求中是+hahaha
,而实际得到的是hahaha
,+
变成了空格。
3. 分析
URL编码有两套编码方式。
- 编码方式1:html4
空格编码为+
,+
编码为%2B
- 编码方式2:RFC-3986
空格编码为%20
,+
编码为%2B
有两个注意点:
- 在一次编解码中,这两个编码方式不能混用,因为空格的编码不一致(其他的没研究)
- RFC-3986不编码直接解码没有问题,html4不编码直接解码不行,会把
+
变空格
3. 解决问题
我们看看我们常用的URL编解码在java代码中的表现,测试代码:
String str = "aaa+aaa aaa";
System.out.println("ENCODE="+URLEncoder.encode(str, "utf-8"));
System.out.println("DECODE="+URLDecoder.decode(str,"utf-8"));
ENCODE=aaa%2Baaa+aaa
DECODE=aaa aaa aaa
可以看到,编码时,+
编码为%2B
,空格编码为+
;解码时,+
变成了空格,空格不变。明显这是采用的html4方式。
到这里基本明白了上面错误的原因,项目中zuul分发时没做url编码,而微服务的springboot默认启用了url解码,将正常的+
变成了空格,从而导致错误。
zuul的配置加上force-original-query-string-encoding: true
,问题解决。
4.tips
- java代码中,如果想指定用RFC-3986编码,使用
org.springframework.web.util.UriUtils
类,或者
URLEncoder.encode("你 好", "utf-8").replaceAll("\\+", "%20");
- 每个浏览器的编码方式不一样,最好自己使用javascript编码,浏览器就不会再编码,使编码统一。
网友评论