怎样解决SpringRestTemplatepost传递参数时报错问题,相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。

成都创新互联-专业网站定制、快速模板网站建设、高性价比大田网站开发、企业建站全套包干低至880元,成熟完善的模板库,直接使用。一站式大田网站制作公司更省心,省钱,快速模板网站建设找我们,业务覆盖大田地区。费用合理售后完善,十年实体公司更值得信赖。
RestTemplate的post参数为什么使用MultiValueMap而不能使用HashMap?
今天跟同事接口联调,使用RestTemplate请求服务端的post接口(使用python开发)。诡异的是,post请求,返回500 Internal Server Error,而使用get请求,返回正常。代码如下:
HashMap hashMap = Maps.newHashMap(); hashMap.put("data", JSONObject.toJSONString(params)); url = "http://mydomain/dataDownLoad.cgi?data={data}"; json = restTemplate.getForObject(url, String.class, hashMap); System.out.println("get json : " + json); url = "http://mydomain/dataDownLoad.cgi"; json = restTemplate.postForObject(url, hashMap, String.class); System.out.println("hasmap post json : " + json);
结果为:
get json : {'status': 0, 'statusInfo': {'global': 'OK'}, 'data': 'http://mydomain/dataDownLoad.cgi?downLoadData=358300d5f9e1cc512efc178caaa0b061'}500 Internal Server Error
最后经过另一位同学帮忙排查,发现RestTemplate在postForObject时,不可使用HashMap。而应该是MultiValueMap。改为如下:
MultiValueMap paramMap = new LinkedMultiValueMap<>();paramMap.add("data", JSONObject.toJSONString(params));url = "http://mydomain/dataDownLoad.cgi";json = restTemplate.postForObject(url, paramMap, String.class);System.out.println("post json : " + json);
结果为:
post json : {'status': 0, 'statusInfo': {'global': 'OK'}, 'data': 'http://mydomain/dataDownLoad.cgi?downLoadData=f2fc328513886e51b3b67d35043985ae'}
然后我想起之前使用RestTemplate发起post请求时,使用POJO作为参数,是可行的。再次测试:
url = "http://mydomain/dataDownLoad.cgi";PostData postData = new PostData();postData.setData(JSONObject.toJSONString(params));json = restTemplate.postForObject(url, paramMap, String.class);System.out.println("postData json : " + json);
返回:500 Internal Server Error。
到现在为止接口调通了。但问题的探究才刚刚开始。
RestTemplate的post参数为什么使用MultiValueMap而不能使用HashMap?
为什么post接口,get请求也可以正确返回?
为什么java服务端可以接收POJO参数,python服务端不可以?python服务端使用CGI(Common Gateway Interface),与cgi有关系吗?
何为MultiValueMap
IDEA中command+N,搜索类MultiValueMap,发现apache的commons-collections包有一个MultiValueMap类,spring-core包中有一个接口MultiValueMap,及其实现类LinkedMultiValueMap。显然看spring包。
首先看LinkedMultiValueMap,实现MultiValueMap接口,只有一个域:Map> targetMap = new LinkedHashMap>()。 其中value为new LinkedList()。再看接口方法:
public interface MultiValueMap extends Map> { V getFirst(K key); //targetMap.get(key).get(0) void add(K key, V value); //targetMap.get(key).add(value) void set(K key, V value); //targetMap.set(key, Lists.newLinkedList(value)) void setAll(Map values); //将普通map转为LinkedMultiValueMap Map toSingleValueMap(); //只保留所有LinkedList的第一个值,转为LinkedHashMap}
综上,LinkedMultiValueMap实际就是Key-LinkedList的map。
RestTemplate怎么处理post参数
首先查看RestTemplate源码,首先将请求封装成HttpEntityRequestCallback类对象,然后再处理请求。
Overridepublic T postForObject(String url, Object request, Class responseType, Object... uriVariables) throws RestClientException { //请求包装成httpEntityCallback RequestCallback requestCallback = httpEntityCallback(request, responseType); HttpMessageConverterExtractor responseExtractor = new HttpMessageConverterExtractor(responseType, getMessageConverters(), logger); //处理请求 return execute(url, HttpMethod.POST, requestCallback, responseExtractor, uriVariables);}
那么HttpEntityRequestCallback是什么样的呢?如下,实际是把请求数据放在了一个HttpEntity中。如果requestBody是HttpEntity类型,就直接转;否则,放在HttpEntity的body中。
//请求内容封装在一个HttpEntity对象中。private HttpEntityRequestCallback(Object requestBody, Type responseType) { super(responseType); if (requestBody instanceof HttpEntity) { this.requestEntity = (HttpEntity) requestBody; } else if (requestBody != null) { this.requestEntity = new HttpEntity