[英]Spring RestTemplate post using parameters in a HashMap throws 400 Bad Request
When I use Spring RestTemplate.postForObject
to post parameters using HashMap
, server throws 400 Bad Request:当我使用 Spring RestTemplate.postForObject
使用HashMap
发布参数时,服务器抛出 400 Bad Request:
Map<String, Object> uriVariables = new HashMap<>();
uriVariables.put("param1", "param1val");
restTemplate.postForObject(url, uriVariables, responseType);
Log:日志:
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)
But if I use MultiValueMap
, it works.但是如果我使用MultiValueMap
,它就可以工作。
MultiValueMap<String, Object> uriVariables = new LinkedMultiValueMap<>();
uriVariables.add("param1", "param1val");
restTemplate.postForObject(url, uriVariables, responseType);
Log:日志:
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"}
Here is controller method:这是控制器方法:
@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);
}
Can somebody explain why passing request parameters using a HashMap
doesn't work?有人可以解释为什么使用HashMap
传递请求参数不起作用吗?
When you say : it throws 400 Bad Request:
do you understand what is referred by it
?当你说: it throws 400 Bad Request:
你明白它指的是it
吗? hint : it is not Spring REST client code, but the server you are talking to, which do not accept your http request as valid.提示:它不是 Spring REST 客户端代码,而是您正在与之交谈的服务器,它不接受您的 http 请求为有效。
You could activate logging of the httpclient implementation used by spring restTemplate to see how going from HashMap
to LinkedMultiValueMap
change the generated http request.您可以激活 spring restTemplate 使用的 httpclient 实现的日志记录,以查看从HashMap
到LinkedMultiValueMap
如何更改生成的 http 请求。
You also could look at spring restTemplate source code to see why generate different requests.您还可以查看 spring restTemplate 源代码以了解为什么会生成不同的请求。
EDIT: the important part of the log you've posted :编辑:您发布的日志的重要部分:
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.161 [main] 调试 org.springframework.web.client.RestTemplate - 使用 [org.springframework.http.converter.json.MappingJackson2HttpMessageConverter@8e24743] 编写 [{param1=1}]
here it is writing json most certainly on the http request body这里肯定是在 http 请求正文上写 json
while with LinkedMultiValueMap :使用 LinkedMultiValueMap 时:
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.502 [main] DEBUG org.springframework.web.client.RestTemplate - 使用 [org.springframework.http.converter.support.AllEncompassingFormHttpMessageConverter@4abdb505] 编写 [{param1=[1]}]
in the first case, your request won't have a parameter param1
but all data in the request body, while in the second case, you get a param1
parameter, making the request valid for the server在第一种情况下,您的请求不会有参数param1
而是请求正文中的所有数据,而在第二种情况下,您会得到一个param1
参数,使请求对服务器有效
It should be possible to configure RestTemplate
by extending / creating a message converter to work the way you want with a HashMap
, but I advise you to stay with LinkedMultiValueMap
for 2 reasons :应该可以通过扩展/创建消息转换器来配置RestTemplate
以使用HashMap
以您想要的方式工作,但我建议您使用LinkedMultiValueMap
有两个原因:
MultiValueMap has different value type than HashMap. MultiValueMap 与 HashMap 具有不同的值类型。 It can be seen in its interface definition:在其接口定义中可以看出:
interface MultiValueMap<K, V> extends Map<K, List<V>>
and HashMap:和哈希映射:
class HashMap<K,V> extends AbstractMap<K,V>
You use put
method to fill HashMap and add
method to fill MultiValueMap, so it looks similar, but in fact MultiValueMap holds lists , not single values.你使用put
方法填充HashMap, add
方法填充MultiValueMap,看起来很相似,但实际上MultiValueMap保存的是lists ,而不是单个值。 It can be seen in your logs:它可以在您的日志中看到:
HashMap:哈希映射:
14:51:20.161 [main] DEBUG org.springframework.web.client.RestTemplate - Writing [{param1=1}] 14:51:20.161 [main] 调试 org.springframework.web.client.RestTemplate - 编写 [{param1=1}]
MultiValueMap:多值映射:
14:52:08.502 [main] DEBUG org.springframework.web.client.RestTemplate - Writing [{param1= 1 }] 14:52:08.502 [main] 调试 org.springframework.web.client.RestTemplate - 编写 [{param1= 1 }]
As you see {param1=1}
does not work and {param1=[1]}
works.如您所见, {param1=1}
不起作用,而{param1=[1]}
起作用。
Additionally, in the documentation of RestTemplate
it is recommended to use MultiValueMap:此外,在RestTemplate
的文档中建议使用 MultiValueMap:
The body of the entity, or request itself, can be a MultiValueMap to create a multipart request.实体的主体或请求本身可以是 MultiValueMap 以创建多部分请求。 The values in the MultiValueMap can be any Object representing the body of the part, or an HttpEntity representing a part with body and headers. MultiValueMap 中的值可以是表示部件主体的任何对象,也可以是表示具有主体和标头的部件的 HttpEntity。 The MultiValueMap can be built conveniently using MultipartBodyBuilder.可以使用 MultipartBodyBuilder 方便地构建 MultiValueMap。
You choice to use HttpHeaders
and HttpEntity
and MultiValueMap
or hashMap
.您可以选择使用HttpHeaders
和HttpEntity
以及MultiValueMap
或hashMap
。 you look up this keyword and link.你查找这个关键字和链接。 may be useful.可能有用。
This is sample code :这是示例代码:
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());
}
For making it to work using HashMap
try this:-要使用HashMap
使其工作,请尝试以下操作:-
List<HttpMessageConverter<?>> messageConverters = new ArrayList<>();
messageConverters.add(new MappingJackson2HttpMessageConverter());
restTemplate.setMessageConverters(messageConverters);
Explanation说明
By default RestTemplate sets a number of MessageConverters like below in the constructor默认情况下,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());
And also it adds automatically MappingJackson2HttpMessageConverter
but that is after AllEncompassingFormHttpMessageConverter
它还自动添加了MappingJackson2HttpMessageConverter
但那是在AllEncompassingFormHttpMessageConverter
之后
which doesn't work(very strange) because AllEncompassingFormHttpMessageConverter
takes over and returns before MappingJackson2HttpMessageConverter
could have actually worked这不起作用(非常奇怪),因为AllEncompassingFormHttpMessageConverter
在MappingJackson2HttpMessageConverter
实际工作之前接管并返回
if (jackson2Present) {
this.messageConverters.add(new MappingJackson2HttpMessageConverter());
}
And the class AllEncompassingFormHttpMessageConverter
extends FormHttpMessageConverter
which is implementing the HttpMessageConverter<MultiValueMap<String, ?>>
, here MultiValueMap
is something to note .类AllEncompassingFormHttpMessageConverter
扩展了FormHttpMessageConverter
,它实现了HttpMessageConverter<MultiValueMap<String, ?>>
,这里MultiValueMap
是需要注意的。
While in the case of MappingJackson2HttpMessageConverter
it extends AbstractJackson2HttpMessageConverter
which is further extending the AbstractGenericHttpMessageConverter<Object>
and note Object
here而在MappingJackson2HttpMessageConverter
的情况下,它扩展了AbstractJackson2HttpMessageConverter
,它进一步扩展了AbstractGenericHttpMessageConverter<Object>
并在此处注意Object
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.