使用Spring RestTemplate进行URL编码数据的POST请求

45

我是Spring的新手,正在尝试使用RestTemplate进行REST请求。Java代码应该与以下curl命令相同:

我对Spring不熟悉,正在尝试使用RestTemplate进行REST请求。Java代码应该与以下curl命令实现相同:

curl --data "name=feature&color=#5843AD" --header "PRIVATE-TOKEN: xyz" "https://someserver.com/api/v3/projects/1/labels"

但是服务器拒绝了RestTemplate,并返回400 Bad Request

RestTemplate restTemplate = new RestTemplate();
HttpHeaders headers = new HttpHeaders();
headers.add("PRIVATE-TOKEN", "xyz");
HttpEntity<String> entity = new HttpEntity<String>("name=feature&color=#5843AD", headers);
ResponseEntity<LabelCreationResponse> response = restTemplate.exchange("https://someserver.com/api/v3/projects/1/labels", HttpMethod.POST, entity, LabelCreationResponse.class);

有人可以告诉我我做错了什么吗?

4个回答

102

我认为问题在于当您尝试向服务器发送数据时,未设置内容类型报头,该报头应为以下两者之一:“application/json”或“application/x-www-form-urlencoded”。在您的情况下,根据您的示例参数(名称和颜色),应使用“application/x-www-form-urlencoded”这个报头意味着“我的客户端向服务器发送什么类型的数据”。

RestTemplate restTemplate = new RestTemplate();

HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
headers.add("PRIVATE-TOKEN", "xyz");

MultiValueMap<String, String> map = new LinkedMultiValueMap<>();
map.add("name","feature");
map.add("color","#5843AD");

HttpEntity<MultiValueMap<String, String>> entity = new HttpEntity<>(map, headers);

ResponseEntity<LabelCreationResponse> response =
    restTemplate.exchange("https://foo/api/v3/projects/1/labels",
                          HttpMethod.POST,
                          entity,
                          LabelCreationResponse.class);

仅仅添加contentType并没有起作用。事实上,人们建议在messageConverter等地方添加contentType。已经起作用的部分如下:userWebTokenHTTPclient = HttpClients.createDefault(); List<NameValuePair> form = new ArrayList<>(); UrlEncodedFormEntity entity = new UrlEncodedFormEntity(form, Consts.UTF_8); httpPost.setEntity(entity);httpPost.addHeader("Content-type", "application/x-www-form-urlencoded");
CloseableHttpResponse tokenResponse = userWebTokenHTTPclient.execute(httpPost);
- veritas

8
您需要将 Content-Type 设置为 application/json。Content-Type 必须在请求中设置。以下是修改后设置 Content-Type 的代码。
final String uri = "https://someserver.com/api/v3/projects/1/labels";
String input = "US";
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
headers.add("PRIVATE-TOKEN", "xyz");
HttpEntity<String> request = new HttpEntity<String>(input, headers);
ResponseEntity<LabelCreationResponse> response = restTemplate.postForObject(uri, request,  LabelCreationResponse.class);

在这里,HttpEntity是使用您的输入“US”和标头构建的。 请告诉我是否有效,如果无效,请分享异常。 干杯!


3
没成功,但我找到了原因。HttpHeader类总是将字符串转换为JSON对象。我必须将内容类型设置为APPLICATION_FORM_URLENCODED。无论如何,谢谢你的帮助。 - Tobi

7
可能是一个标题问题,请检查标题是否有效,您是指“BasicAuth”标题吗?
HttpHeaders headers = new HttpHeaders();
headers.add("Content-Type", MediaType.APPLICATION_FORM_URLENCODED.toString());
headers.add("Accept", MediaType.APPLICATION_JSON.toString()); //Optional in case server sends back JSON data
    
MultiValueMap<String, String> requestBody = new LinkedMultiValueMap<String, String>();
requestBody.add("name", "feature");
requestBody.add("color", "#5843AD");
    
HttpEntity formEntity = new HttpEntity<MultiValueMap<String, String>>(requestBody, headers);
    
ResponseEntity<LabelCreationResponse> response = 
   restTemplate.exchange("https://example.com/api/request", HttpMethod.POST, formEntity, LabelCreationResponse.class);

0
我的问题是,MessageConverters 包含其他可能将实体转换为 JSON 的转换器(例如 FastJsonHttpMessageConverter)。因此,我在前面添加了 FormHttpMessageConverter,它运行良好。
<T> JuheResult<T> postForm(final String url, final MultiValueMap<String, Object> body) {
    HttpHeaders headers = new HttpHeaders();
    headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
    HttpEntity<MultiValueMap<String, Object>> requestEntity = new HttpEntity<>(body, headers);
    return exchange(url, HttpMethod.POST, requestEntity);
}

<T> JuheResult<T> exchange(final String url, final HttpMethod method, final HttpEntity<?> requestEntity) {
    ResponseEntity<JuheResult<T>> response = restTemplate.exchange(url, method, requestEntity,
            new JuheResultTypeReference<>());
    logger.debug("调用结果 {}", response.getBody());
    return response.getBody();
}

public JuheSupplierServiceImpl(RestTemplateBuilder restTemplateBuilder) {
    Duration connectTimeout = Duration.ofSeconds(5);
    Duration readTimeout = Duration.ofSeconds(5);

    restTemplate = restTemplateBuilder.setConnectTimeout(connectTimeout).setReadTimeout(readTimeout)
            .additionalInterceptors(interceptor()).build();
    restTemplate.getMessageConverters().add(0, new FormHttpMessageConverter());
}

fastjson 防止 resttemplate 将除 json 以外的其他 mediaTypes 转换


你好,感谢你的回答。能否请你重新表述一下你的答案,并且解释一下你添加的消息转换器有什么作用? - Eddy
我在旧项目中使用fastjson,但是fastjson有一些“漏洞”破坏了Spring的默认配置‘ /** * 可以序列化/反序列化所有类型。 */ public FastJsonHttpMessageConverter() { super(MediaType.ALL); }’ - ccfish

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接