RestTemplate中的URLEncode

跟进过程与结果在评论

0. 起因

正常情况下 url 只会出现英文字母、数字和标点符号,特殊字符会在请求前进行 encode 操作,转化成合法的 url。 例如我们用浏览器在百度上搜索 += 时,浏览器实际上访问的是 https://www.baidu.com/s?wd=%2B%3D
encode 操作其实是将需要转码的字符转为 16 进制,然后从右到左,取 4 位(不足 4 位直接处理),每 2 位做一位,前面加上 % ,编码成 %XY 格式。
常见特殊字符及编码后值如下:

字符 编码
! %21
# %23
$ %24
% %25
+ %2B
@ %40
: %3A
= %3D
? %3F

1. 经过

在最近的开发中恰好用到了 HTTP 的 GET 方式请求,并且参数中涉及到特殊字符。
在通信过程中发现,第三方服务收到的数据,与我发出的不一致。例如我发出的数据是 a+b= ,第三方收到的却是 a b= ,这就变得有意思了。因为 ‘+’ 和 ‘=’ 都是特殊字符,为什么一个可以正常收到,另一个却不行。
刚开始我们怀疑是日志打印的问题,可是在我开发环境上也能重现这个问题。然后我们开始怀疑是我发出去的请求没有进行 UrlEncode 处理,可是 ‘=’ 却可以被正常接收和处理,经过猜测和跟代码,最后我们终于找到了问题的原因。
找问题的过程如下:
因为我们使用了 Spring 的 RestTemplate 作为 http 的 client ,所以从 RestTemplate 入手。
具体的跟踪思路和源码如下:

  1. RestTemplate 中会有一个 uriTemplateHandler 来处理 uri。
private UriTemplateHandler uriTemplateHandler = new DefaultUriBuilderFactory();
@Override
@Nullable
public <T> T execute(String url, HttpMethod method, @Nullable RequestCallback requestCallback,
        @Nullable ResponseExtractor<T> responseExtractor, Object... uriVariables) throws RestClientException {
   

    URI expanded = getUriTemplateHandler().expand(url, uriVariables);
    return doExecute(expanded, method, requestCallback, responseExtractor);
}
  1. DefaultUriBuilderFactory 会使用 UriComponentsBuilder 来实例化自己。也就是说
/**
 * Default constructor without a base URI.
 * <p>The target address must be specified on each UriBuilder.
 */
public DefaultUriBuilderFactory() {
   
    this(UriComponentsBuilder.newInstance());
}
/**
 * Variant of {@link #DefaultUriBuilderFactory(String)} with a
 * {@code UriComponentsBuilder}.
 */
public DefaultUriBuilderFactory(UriComponentsBuilder baseUri) {
   
    Assert.notNull(baseUri, "'baseUri' is required");
    this.baseUri = baseUri;
}
  1. 而 UriComponentsBuilder 的 encode 处理默认会通过 HierarchicalUriComponents 完成
/**
 * Encode all URI components using their specific encoding rules and return
 * the result as a new {@code UriComponents} instance.
 * @param charset the encoding of the values
 * @return the encoded URI components
 */
@Override
public HierarchicalUriComponents encode(Charset charset) {
   
    if (this.encoded) {
   
        return this;
    }
    String scheme = getScheme();
    String fragment = getFragment();
    String schemeTo = (scheme != null ? encodeUriComponent(scheme, charset, Type.SCHEME) 
  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值