簡體   English   中英

Spring RestTemplate post 在 HashMap 中使用參數會拋出 400 Bad Request

[英]Spring RestTemplate post using parameters in a HashMap 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);

日志:

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傳遞請求參數不起作用嗎?

當你說: it throws 400 Bad Request:你明白它指的是it嗎? 提示:它不是 Spring REST 客戶端代碼,而是您正在與之交談的服務器,它不接受您的 http 請求為有效。

您可以激活 spring restTemplate 使用的 httpclient 實現的日志記錄,以查看從HashMapLinkedMultiValueMap如何更改生成的 http 請求。

您還可以查看 spring restTemplate 源代碼以了解為什么會生成不同的請求。

編輯:您發布的日志的重要部分:

14:51:20.161 [main] 調試 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有兩個原因:

  • 這是默認的彈簧設置。 因此,當您需要更新到下一個版本時,您將可以更輕松地進行遷移。
  • 使用您的代碼的其他人不會對 RestTemplate 使用您的自定義配置的“新”方式感到驚訝

MultiValueMap 與 HashMap 具有不同的值類型。 在其接口定義中可以看出:

interface MultiValueMap<K, V> extends Map<K, List<V>>

和哈希映射:

class HashMap<K,V> extends AbstractMap<K,V>

你使用put方法填充HashMap, add方法填充MultiValueMap,看起來很相似,但實際上MultiValueMap保存的是lists ,而不是單個值。 它可以在您的日志中看到:

哈希映射:

14:51:20.161 [main] 調試 org.springframework.web.client.RestTemplate - 編寫 [{param1=1}]

多值映射:

14:52:08.502 [main] 調試 org.springframework.web.client.RestTemplate - 編寫 [{param1= 1 }]

如您所見, {param1=1}不起作用,而{param1=[1]}起作用。

此外,在RestTemplate文檔中建議使用 MultiValueMap:

實體的主體或請求本身可以是 MultiValueMap 以創建多部分請求。 MultiValueMap 中的值可以是表示部件主體的任何對象,也可以是表示具有主體和標頭的部件的 HttpEntity。 可以使用 MultipartBodyBuilder 方便地構建 MultiValueMap。

您可以選擇使用HttpHeadersHttpEntity以及MultiValueMaphashMap 你查找這個關鍵字和鏈接。 可能有用。

休息模板

頭文件

實體

多值映射

這是示例代碼:

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 + "&timestamp=" + 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());
}

要使用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之后

這不起作用(非常奇怪),因為AllEncompassingFormHttpMessageConverterMappingJackson2HttpMessageConverter實際工作之前接管並返回

if (jackson2Present) {
    this.messageConverters.add(new MappingJackson2HttpMessageConverter());
}

AllEncompassingFormHttpMessageConverter擴展了FormHttpMessageConverter ,它實現了HttpMessageConverter<MultiValueMap<String, ?>>這里MultiValueMap是需要注意的

而在MappingJackson2HttpMessageConverter的情況下,它擴展了AbstractJackson2HttpMessageConverter ,它進一步擴展了AbstractGenericHttpMessageConverter<Object>在此處注意Object

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM