RestTemplate中可变参数传值问题

RestTemplate中可变参数传值问题。
这里说的是指RestTemplate中的http请求方法(get、post等),如:

<T> ResponseEntity<T> getForEntity(String url, Class<T> responseType, Object... uriVariables)
<T> ResponseEntity<T> postForEntity(String url, Object request, Class<T> responseType, Object... uriVariables)
<T> ResponseEntity<T> exchange(String url, HttpMethod method, HttpEntity<?> requestEntity,Class<T> responseType, Object... uriVariables)

这里要说的就是后面这个可变参数:uriVariables
首先,传简8种简单的数据类型是没什么问题的,也不用我多说。然后String也是没有问题的,这个也不用多说。除此之外还会传什么类型的数据呢?个人经验看来有(但不限于)如下几种常见的:

  1. 数组
    比如要根据100个id来查询这100个id对应的对象的信息,我们不可能去请求100次,所以最好的做法就是一次将100个id作为一个数组传过去,然后返回100个对象。

  2. List
    应用场景与上面的一样,将100个id放到一个List中再传过去。

  3. Map
    如果是传一个Map的话,请求方法就变成了:
<T> ResponseEntity<T> getForEntity(String url, Class<T> responseType, Map<String, ?> uriVariables)
<T> ResponseEntity<T> postForEntity(String url, Object request, Class<T> responseType, Map<String, ?> uriVariables)
<T> ResponseEntity<T> exchange(String url, HttpMethod method, HttpEntity<?> requestEntity,Class<T> responseType, Map<String, ?> uriVariables)

此时要注意,Map中的每一个key都要对应url中的一个占位参数,否则会报错。
4. Bean对象


针对问题我们来一步一步的调试代码,看看具体情况,

  • 第一次试验传数组:

这里写图片描述
RestTemplate中的http请求方法最后都会跑到execute方法中去,我们可以看到在execute方法中会去创建请求的URI。创建完成之后就会执行doExecute,那是后话,先不提。我们看创建请求的URI的方法,因为这个方法关系到他是怎样处理我们的请求参数的:
关注一下这个类UriComponents这个类里面有一个内部类:
VarArgsTemplateVariables类
我们的请求参数被此处的VarArgsTemplateVariables构造方法将其构造到迭代器Iterator<Object> valueIterator;中。
下一下关注的类是:HierarchicalUriComponents类,他继承了UriComponents类。这个类里面的expandInternal方法处理了我们的请求参数:
这里写图片描述
注意看,此时这里的迭代器里面的数组还在里面,然后我们看看expandQueryParams方法的实现:
这里写图片描述
下一步:
这里写图片描述
关键的地方到了,关键就是这个getValue:
这里写图片描述
这里写图片描述
好吧,看出来了吧,迭代器的最后的用处在这里,根据每个name(name就是我们的占位的参数名)来取迭代器中的一个值。
原因是,最开始的时候,可变参数本身就是当做数组来处理的,我们在可变参数里面放个数组,其实就相当于送了多个参数了。所以最后得出结论,可变参数中不能送数组。

  • 第二次试验传List:

    execute方法如图:
    这里写图片描述
    下一步是关键,将参数放到迭代器中:
    这里写图片描述
    我们可以看到,这里使用的是Arrays.asList(uriVariableValues).iterator(),如果此时uriVariableValues参数刚好也是个List的话呢?
    这里写图片描述
    我们Object variableValue = uriVariables.getValue(variableName);得到的一个List,被强行toString了,这样的直接结果就是我们传的本来是一个List如[1,2,3,4],结果这个List被转成了String了,http接收方接收到的数据就有问题了。
    结果说明,url传参数时参数是不能传List的。千万不要想着在接收参数的时候手动的去掉中括号,万一List中还包一层List呢。
    同理,如果传的是bean对象,然后在此处toString的话……
    再者说来,bean对象在做get请求的时候一般都是将对象中的有限的几个字段放到url上,而不是将整个对象放到url上。


补充说明,虽然说url上不能传list、数组等,但是现实中有时候却是有要传这些数据的应用场景的(比如上文说的一次传100个id过去),那么怎么办呢?
我们就可以考虑到可以将List、数组、对象等转化成一个json字符串,然后在接收的地方将字符串转回来,这个就能解决这个问题了:

List<String> ids = Lists.newArrayList();
ids.add("1");
ids.add("2");
ids.add("3");
ids.add("4");
String json = gson.toJson(ids);
ResponseEntity<List> entity = restTemplate.getForEntity(url, List.class, json);

在接收参数的地方再将json转回List,就可以了。其他类型的一样的方式处理。
注意:URL请求是有长度限制的。这个不同浏览器长度限制不一样,参数如果太长可能会超出长度限制而报错。

猜你喜欢

转载自blog.csdn.net/u012843361/article/details/79961963