当我使用Spring RestTemplate.postForObject
使用HashMap
发布参数时,服务器会抛出400 Bad Request:
Map<String, Object> uriVariables = new HashMap<>();
uriVariables.put("param1", "param1val");
restTemplate.postForObject(url, uriVariables, responseType);
日志:
14:51:20.102 [main] DEBUG org.springframework.web.client.RestTemplate - Created POST request for "http://localhost:8080/demo-1/test"
14:51:20.113 [main] DEBUG org.springframework.web.client.RestTemplate - Setting request Accept header to [text/plain, application/json, application/*+json, */*]
14:51:20.161 [main] DEBUG org.springframework.web.client.RestTemplate - Writing [{param1=1}] using [org.springframework.http.converter.json.MappingJackson2HttpMessageConverter@8e24743]
14:51:20.225 [main] DEBUG org.springframework.web.client.RestTemplate - POST request for "http://localhost:8080/demo-1/test" resulted in 400 (null); invoking error handler
Exception in thread "main" org.springframework.web.client.HttpClientErrorException: 400 null
at org.springframework.web.client.DefaultResponseErrorHandler.handleError(DefaultResponseErrorHandler.java:78)
at org.springframework.web.client.RestTemplate.handleResponse(RestTemplate.java:700)
at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:653)
at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:613)
at org.springframework.web.client.RestTemplate.postForObject(RestTemplate.java:380)
但如果我使用MultiValueMap
,它就可以了。
MultiValueMap<String, Object> uriVariables = new LinkedMultiValueMap<>();
uriVariables.add("param1", "param1val");
restTemplate.postForObject(url, uriVariables, responseType);
日志:
14:52:08.493 [main] DEBUG org.springframework.web.client.RestTemplate - Created POST request for "http://localhost:8080/demo-1/test"
14:52:08.501 [main] DEBUG org.springframework.web.client.RestTemplate - Setting request Accept header to [text/plain, application/json, application/*+json, */*]
14:52:08.502 [main] DEBUG org.springframework.web.client.RestTemplate - Writing [{param1=[1]}] using [org.springframework.http.converter.support.AllEncompassingFormHttpMessageConverter@4abdb505]
14:52:08.537 [main] DEBUG org.springframework.web.client.RestTemplate - POST request for "http://localhost:8080/demo-1/test" resulted in 200 (null)
14:52:08.539 [main] DEBUG org.springframework.web.client.RestTemplate - Reading [java.lang.String] as "text/plain;charset=UTF-8" using [org.springframework.http.converter.StringHttpMessageConverter@13c27452]
{"name":"1"}
这是控制器方法:
@RequestMapping(name = "/test", method = RequestMethod.POST)
@ResponseBody
public String test(@RequestParam("param1") final String param1) {
// some class Test
final Test test = new Test();
test.setName(param1);
return JsonUtils.toJson(test);
}
有人可以解释为什么使用HashMap
传递请求参数不起作用吗?
答案 0 :(得分:1)
当您说:it throws 400 Bad Request:
时,您是否理解it
引用的内容?提示:它不是Spring REST客户端代码,而是您正在与之通信的服务器,它不接受您的http请求有效。
您可以激活spring restTemplate使用的httpclient实现的日志记录,以查看从HashMap
到LinkedMultiValueMap
的更改如何更改生成的http请求。
您还可以查看spring restTemplate源代码,了解为什么会生成不同的请求。
编辑:您发布的日志的重要部分:
14:51:20.161 [main] DEBUG org.springframework.web.client.RestTemplate - 使用[org.springframework.http.converter.json.MappingJackson2HttpMessageConverter@8e24743]编写[{param1 = 1}]
这里肯定是在http请求主体
上写json 使用LinkedMultiValueMap时:
14:52:08.502 [main] DEBUG org.springframework.web.client.RestTemplate - 使用[org.springframework.http.converter.support.AllEncompassingFormHttpMessageConverter@4abdb505]编写[{param1 = [1]}]
在第一种情况下,您的请求将不会有参数param1
,而是请求正文中的所有数据,而在第二种情况下,您将获得param1
参数,使请求有效服务器
应该可以通过扩展/创建邮件转换器来配置RestTemplate
,以便按HashMap
的方式工作,但我建议您与LinkedMultiValueMap
保持联系有两个原因:
答案 1 :(得分:1)
MultiValueMap具有与HashMap不同的值类型。可以在其界面定义中看到:
interface MultiValueMap<K, V> extends Map<K, List<V>>
和HashMap:
class HashMap<K,V> extends AbstractMap<K,V>
您使用put
方法填充HashMap和add
方法来填充MultiValueMap,因此它看起来很相似,但实际上MultiValueMap包含列表,而不是单个值。它可以在你的日志中看到:
HashMap中:
14:51:20.161 [main] DEBUG org.springframework.web.client.RestTemplate - 写[{param1 = 1}]
MultiValueMap:
14:52:08.502 [main] DEBUG org.springframework.web.client.RestTemplate - 写[{param1 = 1}]
如您所见{param1=1}
不起作用且{param1=[1]}
有效。
此外,在RestTemplate
的{{3}}中,建议使用MultiValueMap:
实体的主体或请求本身可以是MultiValueMap 创建一个多部分请求。 MultiValueMap中的值可以是任意值 表示零件主体的对象,或HttpEntity 表示身体和标题的一部分。 MultiValueMap可以 使用MultipartBodyBuilder方便地构建。
答案 2 :(得分:0)
您可以选择使用HttpHeaders
和HttpEntity
以及MultiValueMap
或hashMap
。你查找这个关键字和链接。可能有用。
这是示例代码:
HttpHeaders headers = new HttpHeaders();
headers.add("x-sample-session-id", sampleService.getServerSessionId());
MultiValueMap<String, Object> map = new LinkedMultiValueMap<String, Object>();
map.add("file", new ByteArrayResource(model.getFile().getBytes()));
map.add("apList", model.getApList());
HttpEntity<?> request = new HttpEntity<Object>(map, headers);
String url = sampleService.getContextPath() + "/thirdParty/" + sampleService.getThirdPartyId() + "/child/"
+ childId + "/location?sessionId=" + childSessionId
+ "&op=sendOnDemandLocationResponseWithPhoto&latitude=" + latitude + "&longitude=" + longitude
+ "&datetime=" + datetime + "&provider=" + provider + "&guardianId=" + guardianId + "&cellInfos="
+ cellInfos + "×tamp=" + timestamp + "&battery=" + battery;
RestTemplate rest = new RestTemplate();
try {
// success
return rest.exchange(url, HttpMethod.POST, request, String.class);
} catch (HttpStatusCodeException e) {
// new Error Response
return ResponseEntity.status(e.getStatusCode()).headers(e.getResponseHeaders())
.body(e.getResponseBodyAsString());
}
答案 3 :(得分:0)
要使用HashMap
使其工作,请尝试以下方法: -
List<HttpMessageConverter<?>> messageConverters = new ArrayList<>();
messageConverters.add(new MappingJackson2HttpMessageConverter());
restTemplate.setMessageConverters(messageConverters);
<强> 解释 强>
默认情况下,RestTemplate在构造函数
中设置一些MessageConverters,如下所示this.messageConverters.add(new ByteArrayHttpMessageConverter());
this.messageConverters.add(new StringHttpMessageConverter());
this.messageConverters.add(new ResourceHttpMessageConverter(false));
this.messageConverters.add(new SourceHttpMessageConverter<>());
this.messageConverters.add(new AllEncompassingFormHttpMessageConverter());
此外,它会自动添加MappingJackson2HttpMessageConverter
,但这是在AllEncompassingFormHttpMessageConverter
这不起作用(非常奇怪),因为AllEncompassingFormHttpMessageConverter
接管并在MappingJackson2HttpMessageConverter
实际工作之前返回
if (jackson2Present) {
this.messageConverters.add(new MappingJackson2HttpMessageConverter());
}
课程AllEncompassingFormHttpMessageConverter
扩展FormHttpMessageConverter
正在实施HttpMessageConverter<MultiValueMap<String, ?>>
,此处MultiValueMap
需要注意。
在MappingJackson2HttpMessageConverter
的情况下,它会延伸AbstractJackson2HttpMessageConverter
,这会进一步扩展AbstractGenericHttpMessageConverter<Object>
和注释Object