问题的提出
基于resttemplate类进行文件下载,但是不幸的url地址中有特殊的转义字符,如果使用resttemplate的方法的话,其默认会进行转义。
原始字符串:
在RestTemplate转义的字符中:
在这个url中%是特殊的字符,并转义为: %25. 但是被转义之后的URL不是正确的URL地址,程序会报出错误的400 BadRequest请求。
尝试与分析
在获取上述信息之后,尝试进行各类处理方法。由于问题解决在RestTemplate中会针对url的string进行encode,所以这个方式的处理都会出现。
主要使用的方式有:
- StringBuffer/StringBuilder,拼接字符(无效)
- URLBuilder生成URI (无效)
在这里注意到URI是不行进行encode的,这是一个发现。 - UriComponent构建uri
- UriComponentBuilder
最终是通过UriComponentBuilder来完美解决问题的,这个是后话。
在RestTemplate的源码分析中发现,其会针对入口的URL String类型进行编码,摘取的代码如下:
代码位置: org.springframework.web.client.RestTemplate之683 ~~ 700行
@Override
@Nullable
public <T> T execute(String url, HttpMethod method, @Nullable RequestCallback requestCallback,
@Nullable ResponseExtractor<T> responseExtractor, Map<String, ?> uriVariables)
throws RestClientException {
URI expanded = getUriTemplateHandler().expand(url, uriVariables);
return doExecute(expanded, method, requestCallback, responseExtractor);
}
@Override
@Nullable
public <T> T execute(URI url, HttpMethod method, @Nullable RequestCallback requestCallback,
@Nullable ResponseExtractor<T> responseExtractor) throws RestClientException {
return doExecute(url, method, requestCallback, responseExtractor);
}
从上述代码中可以发现,其基于URI的方式是不进行encode处理,就是说其将encode操作放入了URI中。
问题的解决
在UriComponentBuilder中提供创建URI的方式,这里就需要看看其是否能够满足要求:
UriComponentsBuilder builder = UriComponentsBuilder
.fromHttpUrl("http://xxx.com/image-checker/train_mean.txt").queryParam("Expires", "3678172563").queryParam("Signature", "2FqOFfzePCjESlKMqiGc9V8C9Es%3D");
log.info("Real File Name:{}, URL:{}", "filetext", builder.build(true).toUri());
从上述代码中,会发现%并未被转义,而是被正确的使用了。
问题的源码分析
经过分析,发现真正进行UriString构建的操作在org.springframework.web.util.HierarchicalUriComponents类中的toUriString()方法中,其直接进行的字符串的拼接,而非进行encode。
感兴趣的读者可以自行进行阅读代码。
参考资料
https://stackoverflow.com/questions/28182836/resttemplate-to-not-escape-url