RestTemplate.exchange() 将“%”转码成“%25”

RestTemplate.exchange() 将“%”转码成“%25”

URL query中包含时间戳,格式yyyy-MM-dd'T'HH:mm:ss'Z',按接口要求对时间戳编码,结果为yyyy-MM-dd'T'HH%3Amm%3Ass'Z',使用RestTemplate.exchange() 时出现问题,RestTemplate会对时间戳二次转码,转码后结果为yyyy-MM-dd'T'HH%253Amm%253Ass'Z',注意两次转码的区别:

第一次第二次
HH%3Amm%3AssHH%253Amm%253Ass

原因就在于exchange()方法会对query转码,%会被转为%25

跟踪代码:

// org.springframework.web.client.RestTemplate
public <T> ResponseEntity<T> exchange(String url, HttpMethod method,
                                      @Nullable HttpEntity<?> requestEntity, Class<T> responseType, Object... uriVariables)
    throws RestClientException {

    // ...
    return nonNull(execute(url, method, requestCallback, responseExtractor, uriVariables));
}

public <T> T execute(String url, HttpMethod method, @Nullable RequestCallback requestCallback,
                     @Nullable ResponseExtractor<T> responseExtractor, Object... uriVariables) throws RestClientException {
	// 注意getUriTemplateHandler()
    URI expanded = getUriTemplateHandler().expand(url, uriVariables);
    return doExecute(expanded, method, requestCallback, responseExtractor);
}

// 最终调用到这里
// org.springframework.web.util.DefaultUriBuilderFactory.DefaultUriBuilder#createUri
private URI createUri(UriComponents uric) {
    if (encodingMode.equals(EncodingMode.URI_COMPONENT)) {
        // 这里做的转码操作
        uric = uric.encode();
    }
    return URI.create(uric.toString());
}

可以看到uric.encode()调用前有个判断,encodingMode是否是TEMPLATE_AND_VALUES

找到原因,解决起来就简单了,不让它进入这个if就行了,查看encodingMode定义:

// org.springframework.web.util.DefaultUriBuilderFactory
private EncodingMode encodingMode = EncodingMode.TEMPLATE_AND_VALUES;

encodingModeDefaultUriBuilderFactory属性,正好有set方法,而getUriTemplateHandler()返回的是RestTemplate的属性:

private UriTemplateHandler uriTemplateHandler;

查看一下DefaultUriBuilderFactory继承关系:
在这里插入图片描述

到这里,解决方法就很明确了:

RestTemplate template = new RestTemplate(factory);
//...

DefaultUriBuilderFactory uriBuilderFactory = new DefaultUriBuilderFactory();
uriBuilderFactory.setEncodingMode(DefaultUriBuilderFactory.EncodingMode.NONE);
template.setUriTemplateHandler(uriBuilderFactory);

总结一下:

  • RestTemplate.exchange()使用uriTemplateHandler处理url
  • uriTemplateHandler的默认实现类是DefaultUriBuilderFactory
  • DefaultUriBuilderFactoryexpand()方法调用其内部类DefaultUriBuilderbuild()方法
  • DefaultUriBuilder.build()根据DefaultUriBuilderFactory.encodingMode决定是否url转码
  • DefaultUriBuilderFactory.encodingMode默认需要转码
  • 因此可以在RestTemplate初始化时手动设置DefaultUriBuilderFactory.encodingMode,不让其转码

搞定!问题解决!

其他解决方法:

出现这个问题时,我也尝试过网上寻找解决方法,但并不适用我的这种情况,也做个记录吧。

若使用RestTemplate.getForObject()出现此问题,可以试试以下方法:

// 直接使用String类型的url会被转码,用URI包装一下就可以了
URI uri = new URI(url);
resp = template.getForObject(uri, String.class);
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
restTemplate.exchangeSpring框架中的一个HTTP客户端工具,用于发送HTTP请求并接收响应。它可以发送各种类型的HTTP请求(GET、POST、PUT、DELETE等),并支持传递请求参数、请求头、请求体等。 使用restTemplate.exchange发送HTTP请求的基本语法如下: ``` ResponseEntity<T> responseEntity = restTemplate.exchange(url, HttpMethod, requestEntity, responseType); ``` 其中,各个参数的含义如下: - url:请求的URL地址。 - HttpMethod:请求的方法类型,例如GET、POST、PUT、DELETE等。 - requestEntity:请求实体,包括请求头、请求体等信息。 - responseType:响应的数据类型。 通过restTemplate.exchange发送HTTP请求后,会返回一个ResponseEntity对象,其中包含了响应的状态码、响应头、响应体等信息。 以下是一个使用restTemplate.exchange发送GET请求的示例: ```java RestTemplate restTemplate = new RestTemplate(); String url = "https://api.example.com/users"; HttpHeaders headers = new HttpHeaders(); headers.set("Authorization", "Bearer token"); HttpEntity<String> requestEntity = new HttpEntity<>(headers); ResponseEntity<String> responseEntity = restTemplate.exchange(url, HttpMethod.GET, requestEntity, String.class); String responseBody = responseEntity.getBody(); ``` 以上示例中,我们发送了一个GET请求到"https://api.example.com/users",并设置了请求头中的Authorization字段。最后,我们通过responseEntity.getBody()获取到了响应体的内容。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值