一、问题背景
项目中使用了RestTemplate来对第三方接口进行请求。 但是使用get方式请求查询接口时,需要将参数拼接到url中,不支持使用JavaBean传参,编写代码不友好。
RestTemplate是一种客户端执行HTTP请求的工具类,封装了基础HTTP客户端库,并提供了简单的模板方法API,帮助程序员方便的发起http请求。
二、实际开发中的做法
在开发中,使用RestTemplate发起Get请求时,通常需要这么写:

上面这种写法是可以正常传参数到服务端的,如果不使用占位符的写法,服务端将收不到参数。 下面这样写就不可以正常传参数了。

服务端接口定义如下,请求结果是参数解析失败,返回400错误码 。

这个时候就想,能不能直接传map,或者一个JavaBean来实现传参数呢?于是查看RestTemplate源码,发现RestTemplate有扩展点可以进行扩展。

三、RestTemplate扩展接口说明
| 扩展接口 |
说明 |
ResponseErrorHandler |
用于确定特定响应是否有错误的策略接口 |
ClientHttpRequestInterceptor |
拦截客户端HTTP请求,支持设置多个 |
HttpMessageConverter |
策略接口,指定可以在HTTP请求和响应之间进行转换的转换器 |
ClientHttpRequestFactory |
这个是对通信定制接口,支持okhttp,httpcompoent,netty等 |
UriTemplateHandler |
定义用于使用变量扩展URI模板的方法。 |
上面5个扩展接口中,UriTemplateHandler是本文采用的,通过参数对uri进行重构,自动拼接参数到url中。
四、扩展实践
查看RestTemplate的UriTemplateHandler扩展接口,它只有两个方法,专门处理请求url的。
public interface UriTemplateHandler {//这个方法参数uriTemplate是我们请求的地址,map就是我们传入的参数mapURI expand(String uriTemplate, Map<String, ?> uriVariables);URI expand(String uriTemplate, Object... uriVariables);}
于是自定义一个UriTemplateHandler,来对请求url进行处理。
代码如下:
public class GetUriTemplateHandler implements UriTemplateHandler {//组合默认的uri处理器private UriTemplateHandler uriTemplateHandler = new DefaultUriBuilderFactory();public URI expand(String uriTemplate, Map<String, ?> uriVariables) {//利用UriComponentsBuilder将请求参数追加到url,GetParamMultiValueMap是自定义的一个mapuriTemplate = UriComponentsBuilder.fromHttpUrl(uriTemplate).queryParams(new GetParamMultiValueMap(uriVariables)).toUriString();log.info("处理之后的url ==> {}", uriTemplate);return uriTemplateHandler.expand(uriTemplate, uriVariables);}public URI expand(String uriTemplate, Object... uriVariables) {return uriTemplateHandler.expand(uriTemplate, uriVariables);}}
GetParamMultiValueMap实现如下:
public class GetParamMultiValueMap<K,V> implements MultiValueMap<K, V> {private final Map<K, List<V>> map;public GetParamMultiValueMap(Map<K, V> paramMap) {Assert.notNull(paramMap, "'map' must not be null");//将请求参数转换成MultiValueMapthis.map = new HashMap<>();paramMap.forEach((k, v) -> {if (v instanceof List) {map.put(k, (List<V>) v);} else {map.put(k, Lists.newArrayList(v));}});}}// 此处省略其他map操作方法.....
GetUriTemplateHandler实现好之后,只需要在RestTemplate实例化之后将默认的UriTemplateHandler替换就可以使用自己实现的功能了
public class RestTemplateConfig {(value = RestTemplate.class)public RestTemplate restTemplate() {RestTemplate restTemplate = new RestTemplate();//默认使用的jdk的http请求组件 替换成okhttp3restTemplate.setRequestFactory(new OkHttp3ClientHttpRequestFactory());//使用自定义的uri处理器,实现参数追加到url的功能restTemplate.setUriTemplateHandler(new GetUriTemplateHandler());return restTemplate;}}
下面再看测试结果:

从测试结果看,自定义的uri处理器已经将参数拼接好了。并且收到了服务端的正确返回。

五、再次扩展
上面已经实现将map参数拼接到url参数了,现在还是传递的map,能不能直接传一个JavaBean呢?
答案是可以的,通过对RestTemplate进行包装,借助apache-commont包将JavaBean转换成map,代码如下:

将增强的的RestTemplate注入到Spring容器中:
public RestTemplateWrapper restTemplateWrapper(RestTemplate restTemplate) {return new RestTemplateWrapper(restTemplate);}
测试是否可以通过bean传递参数成功。

从结果看,参数可以拼接到url,并且可以成功接收服务的响应 。
ok receive message ===> test上面内容就是基于RestTemplate对Get请求参数扩展的处理了,如果有更多的实践方案欢迎一起探讨。
原文作者:服务端技术栈;如有侵权,请联系管理员删除。