记一次 RestTemplate 请求失败问题的排查 → RestTemplate 默认会对特殊字符进行转义

这篇具有很好参考价值的文章主要介绍了记一次 RestTemplate 请求失败问题的排查 → RestTemplate 默认会对特殊字符进行转义。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

开心一刻

  今天中午,侄子在沙发上玩手机,他妹妹屁颠屁颠的跑到他面前

  小侄女:哥哥,给我一块钱

  侄子:叫妈给你

  小侄女朝着侄子,毫不犹豫的叫到:妈!

  侄子:不是,叫妈妈给你

  小侄女继续朝他叫到:妈妈

  侄子受不了,从兜里掏出一块钱说道:我就只有这一块钱了,拿去拿去

  小侄女最后还不忘感谢到:谢谢妈妈!

  侄子彻底奔溃了,我在一旁笑出了鹅叫声

记一次 RestTemplate 请求失败问题的排查 → RestTemplate 默认会对特殊字符进行转义

需求背景

  需求很简单,就是以 HTTP 的方式下载 OSS 上的文件,类似如下

记一次 RestTemplate 请求失败问题的排查 → RestTemplate 默认会对特殊字符进行转义

  分两步

  1、获取文件的下载地址( HTTP 地址 )

  2、根据下载地址下载文件

  第 1 步不是本文的重点,略过,我们只需要实现第 2 步,是不是很简单?

问题复现

  目前,系统跟其他系统的 HTTP 对接都是用的 RestTemplate 

  那毫无疑问,也用 RestTemplate 来下载 OSS 文件

  测试代码非常简单,如下

记一次 RestTemplate 请求失败问题的排查 → RestTemplate 默认会对特殊字符进行转义记一次 RestTemplate 请求失败问题的排查 → RestTemplate 默认会对特殊字符进行转义
package com.qsl;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.ResponseEntity;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.web.client.RestTemplate;

import javax.annotation.Resource;

/**
 * @description: RestTemplate 测试
 * @author: 博客园@青石路
 * @date: 2023/11/26 15:31
 */
@RunWith(SpringRunner.class)
@SpringBootTest
public class RestTemplateTest {

    @Resource
    private RestTemplate restTemplate;

    @Test
    public void testOss() {
        String ossUrl = "https://qsl-yzb-test.oss-cn-wuhan-lr.aliyuncs.com/company_compare_t.sql?Expires=1700987277&OSSAccessKeyId=TMP.3Kf7vKYWL9RHkroENy7hUyrqAhHBC8YpBCnqXAstCyH3K1j6fkZujtL47V1mFkG5e5hmnLD2dVn4ZJGeD2yDh3GAAQc1k8&Signature=O2qiPYvfZyPmeouwzkXcNqC4Oy0%3D";
        ResponseEntity<byte[]> responseEntity = restTemplate.getForEntity(ossUrl, byte[].class);
        System.out.println(responseEntity.getStatusCode());
    }
}
View Code

记一次 RestTemplate 请求失败问题的排查 → RestTemplate 默认会对特殊字符进行转义

  我们看下执行结果,发现报异常了

记一次 RestTemplate 请求失败问题的排查 → RestTemplate 默认会对特殊字符进行转义记一次 RestTemplate 请求失败问题的排查 → RestTemplate 默认会对特殊字符进行转义
org.springframework.web.client.HttpClientErrorException$Forbidden: 403 Forbidden: [<?xml version="1.0" encoding="UTF-8"?>
<Error>
  <Code>AccessDenied</Code>
  <Message>Request has expired.</Message>
  <RequestId>65630E3B05EC713334EDD93D</RequestId>
  <HostId>qsl-yzb-test.oss-cn-wuh... (443 bytes)]

    at org.springframework.web.client.HttpClientErrorException.create(HttpClientErrorException.java:109)
    at org.springframework.web.client.DefaultResponseErrorHandler.handleError(DefaultResponseErrorHandler.java:170)
    at org.springframework.web.client.DefaultResponseErrorHandler.handleError(DefaultResponseErrorHandler.java:112)
    at org.springframework.web.client.ResponseErrorHandler.handleError(ResponseErrorHandler.java:63)
    at org.springframework.web.client.RestTemplate.handleResponse(RestTemplate.java:785)
    at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:743)
    at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:677)
    at org.springframework.web.client.RestTemplate.getForEntity(RestTemplate.java:345)
    at com.qsl.RestTemplateTest.testOss(RestTemplateTest.java:27)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
    at org.springframework.test.context.junit4.statements.RunBeforeTestExecutionCallbacks.evaluate(RunBeforeTestExecutionCallbacks.java:74)
    at org.springframework.test.context.junit4.statements.RunAfterTestExecutionCallbacks.evaluate(RunAfterTestExecutionCallbacks.java:84)
    at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:75)
    at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:86)
    at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:84)
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:251)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:97)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
    at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
    at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:190)
    at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
    at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
    at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:33)
    at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:230)
    at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:58)
View Code

记一次 RestTemplate 请求失败问题的排查 → RestTemplate 默认会对特殊字符进行转义

  直接从浏览器下载是正常的,用代码走 RestTemplate 方式下载则失败,提示 403 Forbidden 

  是不是有点懵?

记一次 RestTemplate 请求失败问题的排查 → RestTemplate 默认会对特殊字符进行转义

问题排查

  系统中已经用 RestTemplate 对接了很多 HTTP 接口,全部都没问题

  这不就是一个很简单的 HTTP 请求吗,简单的不能再简单了,怎么会失败了?

  直接把我整不会了,不知道从何下手去排查了

记一次 RestTemplate 请求失败问题的排查 → RestTemplate 默认会对特殊字符进行转义

  第一时间想到了阿里云 OSS 售后,联系到人工客服,反馈了问题

  客服响应倒是很及时,但却迟迟没有找到问题原因

  然后我又将求助目光转向了部门内同事

  有个同事提到:你开启 debug 日志,看看 RestTemplate 请求地址或参数是不是有什么问题

  我内心其实是拒绝的, HTTP 地址都是现成的,都不用拼接, GET 方式的参数也是直接在 URL 中,能有什么问题?

  但我的手却很诚实,默默的开启了 debug 日志(在配置文件中加上: debug: true )

  执行结果依旧失败,但是多了三行 debug 日志

记一次 RestTemplate 请求失败问题的排查 → RestTemplate 默认会对特殊字符进行转义

   RestTemplate 的请求 URL 已经打印出来了,我们来和原始的 URL 对比一下,看看是不是有区别

记一次 RestTemplate 请求失败问题的排查 → RestTemplate 默认会对特殊字符进行转义

   不比不知道,一比吓一跳,这特喵的 RestTemplate 是做了手脚呀!对 % 进行了转义处理,处理成 %25 了

  至于为什么需要对 GET 方式的 URL 的特殊字符进行转义,我就不做过多解释了(网上资料很多!),举个例子你们就明白了

     http://localhost:8080/hello?name=青石路 的参数 name 的值是 青石路 ,这个大家都认可吧?

    如果 name 的值是 青石路&路石青 ,这个 URL 应该是怎样的?

    有人可能会有疑问了:你这说的是 &,跟 % 有什么关系?

    你是黑子,来搞我的吧?

    求求你别搞我,我很菜的!

记一次 RestTemplate 请求失败问题的排查 → RestTemplate 默认会对特殊字符进行转义

     RFC 3986编码规范 指明了:百分号本身用作对不安全字符进行编码时使用的特殊字符,因此本身需要编码

    例如: %20 表示空格, %2B 表示 +,等等

问题处理

  问题已经找到了,那么该如何处理了?

  抛开上面的问题,处理这种 URL 转义的问题,方式有很多

  1、改成 POST 请求方式

    比较推荐这种方式,奈何这种方式不适用本案例

  2、使用 HttpClient jar

    因为同事用的这种方式实现与本案例一样的下载,没有转义问题

    但为了统一,仍想保留统一的 RestTemplate 方式,即没有采用这种方式

  3、 RestTemplate 的 URI 方式

记一次 RestTemplate 请求失败问题的排查 → RestTemplate 默认会对特殊字符进行转义

    本案例最终采用这种的方式

记一次 RestTemplate 请求失败问题的排查 → RestTemplate 默认会对特殊字符进行转义

    通过 debug 日志是能够看到, RestTemplate 请求的地址是没有进行转义的(这里不展示了,大家自行去测试!)

    至于 String 和 URI 的差别,大家去 debug 跟下源码就清楚了,底层的实现差别还是很大的哦

  当然还有其他的方式,但是需要结合系统当前的情况,找出最合适的那种方式

总结

  1、别自以为是,该试还得试

  2、 debug 日志是调试的好东西,记得用、用、用!

  3、多学多总结,多和同事分享沟通,有问题了才好请教他们

记一次 RestTemplate 请求失败问题的排查 → RestTemplate 默认会对特殊字符进行转义

 文章来源地址https://www.toymoban.com/news/detail-748349.html

到了这里,关于记一次 RestTemplate 请求失败问题的排查 → RestTemplate 默认会对特殊字符进行转义的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 记一次Apache HTTP Client问题排查

    通过日志查看,存在两种异常情况。 第一种:开始的时候HTTP请求会报超时异常。 762663363 [2023-07-21 06:04:25] [executor-64] ERROR - com.xxl.CucmTool - CucmTool|sendRisPortSoap error,url:https://xxxxxx/realtimeservice/services/RisPort org.apache.http.conn.HttpHostConnectException: Connect to xxx [/xxx] failed: 连接超时 第二种

    2024年02月12日
    浏览(49)
  • 记一次jedis连接池顽固问题排查与修改

    这辈子不想再看到jedisBrokenPipe!!   测试环境运行16天后报错信息: 05:42:32.629 [http-nio-8093-exec-2] ERROR o.a.c.c.C.[.[.[.[dispatcherServlet] - [log,175] - Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is redis.clients.jedis.exceptions.JedisCon

    2023年04月21日
    浏览(45)
  • 记一次 .Net+SqlSugar 查询超时的问题排查过程

    环境和版本:.Net 6 + SqlSuger 5.1.4.*   ,数据库是mysql 5.7 ,数据量在2000多条左右 业务是一个非常简单的查询,代码如下: tb_name 下配置了一对多的关系导航,但是执行时没有include导航属性,当执行上述代码时,查询非常慢,甚至会超时报错: The Command Timeout expired before the o

    2024年02月07日
    浏览(34)
  • 记一次Oracle归档日志异常增长问题的排查过程

    Oracle归档日志是Oracle数据库的重要功能,用于将数据库的重做日志文件(Redo Log)保存到归档日志文件(Archive Log)中。归档日志的作用是提供数据库的备份和恢复功能,以及支持数据库的持续性和数据完整性。 当数据库处于归档模式时,数据库引擎会将已经写满的重做日志

    2024年02月14日
    浏览(42)
  • 记一次 MySQL timestamp 精度问题的排查 → 过程有点曲折

    下午正准备出门,跟正刷着手机的老妈打个招呼 我:妈,今晚我跟朋友在外面吃,就不在家吃了 老妈拿着手机跟我说道:你看这叫朋友骗缅北去了,tm血都抽干了,多危险 我:那是他不行,你看要是吴京去了指定能跑回来 老妈:还吴京八经的,特么牛魔王去了都得耕地,唐

    2024年02月01日
    浏览(44)
  • 记一次线上bug排查-----SpringCloud Gateway组件 请求头accept-encoding导致响应结果乱码

           基于公司的业务需求,在SpringCloud Gateway组件的基础上,写了一个转发服务,测试开发阶段运行正常,并实现初步使用。但三个月后,PostMan请求接口,返回异常,经排查,从日志中获取到转发响应的结果为乱码:        跟踪日志: 转发到目标接口,响应结果已乱码

    2024年02月04日
    浏览(42)
  • 记一次MySQL5初始化被kill的问题排查

    由于测试环境JED申请比较繁琐,所以Eone提供了单机版Mysql供用户使用,近期Eone搭建Mysql5的时候发现莫名被kill了,容器规格是4C8G,磁盘30G 这不科学,之前都是可以的,镜像没变,配置没变,咋就不行了呢,一定不是我的问题,是机器的问题 通过多次搭建mysql5进行采样,发现

    2024年02月08日
    浏览(36)
  • 记一次 Redisson 线上问题 → ERR unknown command 'WAIT' 的排查与分析

    昨晚和一个朋友聊天 我:处对象吗,咱俩试试? 朋友:我有对象 我:我不信,有对象不公开? 朋友:不好公开,我当的小三 程序在生产环境稳定的跑着 直到有一天,公司执行组件漏洞扫描,有漏洞的  jar  要进行升级修复 然后我就按着扫描报告将有漏洞的  jar  修复到指

    2024年02月09日
    浏览(43)
  • 记一次dubbo消费者注册失败找不到服务提供者问题

    项目分多套环境,其中一套环境重新部署时,频繁出现消费者找不到服务提供者的错误 经过多次重启后才有可能恢复正常,而其他环境并没有发现此问题 怀疑点: 1.消费者和服务提供者dubbo版本对不上 2.服务提供者没有注册上服务 3.注册中心有问题 逐一排查: 1.消费者和服

    2023年04月18日
    浏览(28)
  • 记一次排查:接口返回值写入excel后,从单元格copy出来的数据会带有多重引号的问题

    在项目里刚好有3个服务,同一个网关内层的3个服务,两个php的,一个golang的,为了提高负载以及进行分流,部分客户的接口调用会被网关自动分配到go服务。 恰好为了测试,我写了一个全量用户的生产、测试环境调用接口返回结果进行对比的脚本,于是发现了题中的问题:

    2024年02月05日
    浏览(33)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包