都是Get请求,RestTemplate和URL不一样?

0. 背景

最近接到一个对接变更支持需求。业务场景是:我们去获取对方系统的附件。现有的交互逻辑是:

  1. 访问对方的接口,返回一个url链接,这个链接包含鉴权和附件信息,鉴权信息包含有效期
  2. 访问步骤1返回的url,返回IO流,从而获得附件

对接系统即将发生一个变更,即变更url链接,变更后的url也是返回IO流。于是笔者想,那这不关我们事啊,你保证url可以正常返回IO流就可以,毕竟步骤1的接口又没有变。

于是对方在测试环境开发后开始同我们联调,不出意外的话就是出意外了。。

死活就是获取不到附件,获取到的附件无法正常打开,使用VS Code打开后里面是错误的提示信息,提示鉴权不通过。

而现有的获取IO流的方式是使用RestTemplate,然后对接的同事让我用URL试试,说他们自己也是这么用的,结果就可以了。

在这里插入图片描述

1. 模拟

这里就省去具体的业务场景,也省去期间的各种推测、验证了,直接模拟最终的结果。

1.1 请求的url

先说明下get请求的url:

http://localhost:8080/tool/attachment/getIO/JhKYanCAt1NPTzlbSHgO8civur5LBZqy?signature=QUJD+REVGRw==&content=attachment%3Bfilename%3D%E9%83%BD%E6%98%AFGet%E8%AF%B7%E6%B1%82.md

说明如下,为了方便演示,请求参数设置为两个,主要是附件的信息和权限校验:

内容参数说明
API接口/tool/attachment/getIO获取附件IO流
路径参数 - fileIdJhKYanCAt1NPTzlbSHgO8civur5LBZqy附件id
请求参数 - signatureQUJD+REVGRw==签名
请求参数 - contentattachment…(省略)内容,看到很多%就推测是做了URL编码

1.2 服务端

import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;

/**
 * Copyright: Horizon
 *
 * @ClassName AttachmentController
 * @Description 附件控制层
 * @Author Nile (QQEmail:576109623)
 * @Date 12:56 2023/5/13
 * @Version 1.0.0
 */
@Slf4j
@RestController
@RequestMapping("/tool/attachment")
public class AttachmentController {

    /**
     * 获取附件IO流
     * @author Nile (QQEmail:576109623)
     * @date 12:56 2023/5/13
     * @param fileId 附件id
     * @param signature 签名
     * @param content 内容
     * @return void
     */
    @GetMapping("/getIO/{fileId}")
    public String getAttachmentIO(@PathVariable String fileId, @RequestParam String signature,
                                  @RequestParam String content) {
        log.info("fileId: {}", fileId);
        log.info("signature: {}", signature);
        log.info("content: {}", content);
        // 返回IO流,简单返回个提示
        return "SUC0000";
    }
}

1.3 客户端

package com.project.tool.controller;

import lombok.extern.slf4j.Slf4j;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.core.io.Resource;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.ResponseEntity;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.web.client.RestTemplate;

import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;

import static org.springframework.http.HttpHeaders.CONNECTION;
import static org.springframework.http.HttpHeaders.CONTENT_TYPE;

/**
 * Copyright: Horizon
 *
 * @ClassName AttachmentControllerTest
 * @Description 附件测试类
 * @Author Nile (QQEmail:576109623)
 * @Date 13:26 2023/5/13
 * @Version 1.0.0
 */
@Slf4j
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@RunWith(SpringRunner.class)
public class AttachmentControllerTest {
    private static final String REQUEST_URL = "http://localhost:8080/tool/attachment/getIO" +
            "/JhKYanCAt1NPTzlbSHgO8civur5LBZqy?signature=QUJD+REVGRw==&content=attachment%3Bfilename%3D%E9%83%BD%E6" + "%98%AFGet%E8%AF%B7%E6%B1%82.md";

    /**
     * 使用RestTemplate请求
     * @Author Nile (QQEmail:576109623)
     * @Date 16:39 2023/5/13
     */
    @Test
    public void getAttachmentWithRestTemplate() throws IOException {
        HttpHeaders headers = new HttpHeaders();
        headers.set(CONNECTION, "Keep-Alive");
        headers.set(CONTENT_TYPE, "charset=UTF-8");
        HttpEntity<Void> httpEntity = new HttpEntity<>(headers);
        RestTemplate restTemplate = new RestTemplate();
        ResponseEntity<Resource> response = restTemplate.getForEntity(REQUEST_URL, Resource.class, httpEntity);
        Resource resource = response.getBody();
        InputStream inputStream = resource.getInputStream();
    }

    /**
     * 使用URL请求
     * @Author Nile (QQEmail:576109623)
     * @Date 16:39 2023/5/13
     */
    @Test
    public void getAttachmentWithURL() throws IOException {
        URL urlObject = new URL(REQUEST_URL);
        URLConnection urlConnection = urlObject.openConnection();
        urlConnection.connect();
        InputStream inputStream = urlConnection.getInputStream();
    }
}

注:这里使用了两种方式进行get请求,其实还有第三种方式,就是浏览器直接访问这个url链接,毕竟在很多的业务场景下,需要点击直接下载附件,使用的也是get请求。

2. 模拟结果

直接说模拟结果了,其实就是url编码的问题。

请求的参数:

参数
fileIdJhKYanCAt1NPTzlbSHgO8civur5LBZqy
signatureQUJD+REVGRw==
contentattachment%3Bfilename%3D%E9%83%BD%E6%98%AFGet%E8%AF%B7%E6%B1%82.md

服务端接收到的参数:

请求方式路径参数 - fileId请求参数 - signature请求参数 - content
RestTemplateJhKYanCAt1NPTzlbSHgO8civur5LBZqyQUJD REVGRw==attachment%3Bfilename%3D%E9%83%BD%E6%98%AFGet%E8%AF%B7%E6%B1%82.md
URLJhKYanCAt1NPTzlbSHgO8civur5LBZqyQUJD REVGRw==attachment;filename=都是Get请求.md
ChromeJhKYanCAt1NPTzlbSHgO8civur5LBZqyQUJD REVGRw==attachment;filename=都是Get请求.md

3. 结论

虽然上面的路径参数是UUID,但因为好奇,笔者还是换成了URL编码后的参数和带加号的参数做了测试。

  1. 对于路径参数,服务端原样接收,不会对参数进行URL解码,+也不会变为空格

  2. 对于请求参数:

    a. RestTemplate,不会对参数进行URL解码,会原样传到服务端;

    b. URL和Chrome,会对参数进行URL解码,服务端接收到的是解码后的数据;

    c. 不论那种方式,+都会变为空格。(笔者之前总结过一篇日志:浏览器对url字符的处理,请参考指正)

好吧,也终于知道为什么对接系统给的新链接的参数要进行URL编码,就是为了防止+等特殊字符的转变问题。而使用RestTemplate因为没有进行URL解码,导致服务端接收的参数不对(服务端不会对接收到的参数进行解码),这就是为什么错误信息是鉴权不通过。不得已只能变更请求方式,进行了一次上线。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值