找回密码
 立即注册
搜索
热搜: 活动 交友 discuz
查看: 285|回复: 0

[博客归档]RestTemplate 请求URL编码问题

[复制链接]

378

主题

55

回帖

2063

积分

管理员

积分
2063
发表于 2022-2-28 09:33:05 | 显示全部楼层 |阅读模式

在通过第三方接口获取数据文件下载地址后,通过RestTemplate直接去下载

发现会出现间接性异常,后来排查发现

会对传入的url参数选择性编码???

对%进行转义,但是对/+需要转义的字符视而不见,导致请求接口的时候报错

引用:https://www.cnblogs.com/codeToSuccess/p/13906192.html

一、遇到问题

今天要写一个接口,收到请求后,给第三方接口发送请求,第三方接口会创建一个聊天室,然后返回报文。

碰到一个问题:

使用restTemplate.getForObject()发送请求时,获取的响应报文显示:{"message":"url请求非法!"},无法获取正确的响应报文。

代码如下:

//样例urlString url = "http://10.111.222.333/live";String cid = "USER_NAME_EXAMPLE";String signCode = "LIKEPASS123ABC456";String roomId = "@ABC#1A2B3C";String tail = "/chatRoom/create_room?cid="+cid+"&timestamp="+System.currentTimeMillis()+"&roomId="+URLEncoder.encode(roomId);String sign = "&sign="+signCode;String s = MD5Util.computeMD5(tail+sign);//获得最终的urlurl = url + tail + "&md5=" + s;RestTemplate restTemplate = new RestTemplate();//发送请求并用String格式获取响应报文String backStr = restTemplate.getForObject(url, String.class);//将响应报文转为JSONObject格式JSONObject backJson = JSONObject.fromObject(backStr);//打印下请求地址,urlSystem.out.println(url);//打印下响应报文,backStrSystem.out.println(backStr);

调用接口后,收到的错误的响应报文:

{"message":"url请求非法!","code":"-1"}二、分析问题

1.首先,第三方的接口没有问题。(虽然不知道什么情况下会返回这种信息)

2.将控制台打印出来的url复制,用Chrome打开是没有问题的,页面也可以看到"创建聊天室成功"的json报文。(本质上就是一个普通的get请求,也不是请求头设置错误)

3.同样的url,在程序中就无法获取正确的响应报文。

4.准备抓包分析。

(1)首先打开抓包工具Fiddler,用chrome访问url,获得正确的包;

(2)在代码中增加配置,使用代理:

System.setProperty("http.proxyHost", "127.0.0.1");System.setProperty("https.proxyHost", "127.0.0.1");System.setProperty("http.proxyPort", "8888");System.setProperty("https.proxyPort", "8888");

(3)打开Fiddler,Tools->Fiddler Options...->Connections,确认代理端口是否为8888(与代码中的要匹配)

(4)调试代码,抓包,与正确的包比较,发现了不同点:

//正确的urlhttp://10.111.222.333/live/chatR ... =b0f35all1k3j241l//错误的urlhttp://10.111.222.333/live/chatR ... d5=b0f35all1k3j241l

(5)不同点是roomId的值。

三、明确问题

1.代码中,为了获取md5码,需要先使用URLEncoder.encode(roomId)对roomId转码,然后拼接成url,这本来是没有问题的。

2.然而在使用restTemplate.getForObject(url, String.class)发送请求时,它对url中的【%】又进行了一次encode转码,导致【%】被转为了【%25】,进一步导致实际请求的url错误,第三方接口也就返回了“url请求非法!”的错误信息。

四、解决方法

1.首先想到的解决方法是,既然restTemplate在发送请求时会对url进行encode转码,那么在拼接url时自己先不转码,就不会有问题了;然而这是一个坑:

//代码同上,省略......//使用tail2,不encode,进行url拼接String tail2 = "/chatRoom/create_room?cid="+cid+"&timestamp="+System.currentTimeMillis()+"&roomId="+roomId;//获得最终的urlurl = url + tail2 + "&md5=" + s;

通过抓包发现,实际请求的url还是有问题:

//控制台打印的url,roomId没有encodehttp://10.111.222.333/live/chatR ... =b0f35all1k3j241l//我们认为的正确url,restTemplate发送请求时会对url进行encode,将@和#转码了http://10.111.222.333/live/chatR ... =b0f35all1k3j241l//实际抓包得到的url,还是请求了一个错误的路径http://10.111.222.333/live/chatR ... 156&roomId=@ABC

可以看到,如果我们自己不对url进行encode的话,restTemplate进行处理时会有问题,丢失了【#】之后的字段,并且没有对【@】进行encode。

经过多次测试,restTemplate会将url中的【%】转为【%25】,其它特殊字符如【!@#】等,则不会转码。

如果我们自己对url进行了encode,此时特殊符号被转为了【%+数字或字母】的形式;然而,restTemplate进行处理时还会将【%】转为【%25】,实际请求的url还是错误的。

2.踩过坑之后,下面是正确的解决方式:

//上方代码同上,可以执行自己的encode......URI uriObj = URI.create(url);RestTemplate restTemplate = new RestTemplate();//之前是传入String类型的url//现在改为URI类型的uriObj,这样restTemplate就不会进行encode了。String backStr = restTemplate.getForObject(uriObj, String.class);五、总结

使用restTemplate发送请求时,如果传入String类型的url,则这个url中的【%】会被转为【%25】;

如果url中不存在特殊符号,则没有问题;

如果url存在特殊符号且自己没有encode,那么restTemplate执行后,实际请求的url可能会有问题(会丢失#后面的内容等,url机制);

如果url中存在特殊符号且自己执行过encode,那么restTemplate执行后,url会被再次encode,导致实际请求的url出错(%被转码)。

因此,如果url中存在特殊符号,可以在自己执行encode后,转为URI对象,再使用restTemplate发送请求,就避免了【%】被转码的问题。

<补充:URLEncoder.encode()是将字符转为URL可以使用的编码的方法,一般来说,URL只能使用英文字母、阿拉伯数字和某些标点符号,不能使用其他文字和符号。>


回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

Archiver|手机版|小黑屋|Comsenz Inc.

GMT+8, 2024-12-19 22:36 , Processed in 0.057633 second(s), 19 queries .

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表