防止Spring的RestTemplate在multipart/form-data中为每个参数添加标题

3
我需要使用Spring的RestTemplate调用一个需要POST请求且Content-Type为multipart/form-data的外部API。输入数据只有键值对,没有附件,但服务器强制我使用multipart/form-data。
以下是正常工作的原始请求。
POST http://the-api:8080 HTTP/1.1
Content-Type: multipart/form-data; boundary=--Eh0oKOHPOSEIJTzFevDxHhPNKhQl7AP6kQL
Accept: */*
Host: the-api:8080
accept-encoding: gzip, deflate
content-length: 680
Connection: keep-alive

--Eh0oKOHPOSEIJTzFevDxHhPNKhQl7AP6kQL
Content-Disposition: form-data; name="param1"

value1
--Eh0oKOHPOSEIJTzFevDxHhPNKhQl7AP6kQL
Content-Disposition: form-data; name="param2"

value2
--Eh0oKOHPOSEIJTzFevDxHhPNKhQl7AP6kQL--

以下是我从RestTemplate的日志中提取和重新排列的原始请求,由于服务器错误地将标头视为值而未能成功。
POST http://the-api:8080 HTTP/1.1
Content-Type: multipart/form-data; boundary=--Eh0oKOHPOSEIJTzFevDxHhPNKhQl7AP6kQL
Accept: */*
Host: the-api:8080
accept-encoding: gzip, deflate
content-length: 680
Connection: keep-alive

--Eh0oKOHPOSEIJTzFevDxHhPNKhQl7AP6kQL
Content-Disposition: form-data; name="param1"
Content-Type: text/plain;charset=UTF-8
Content-Length: 29

value1
--Eh0oKOHPOSEIJTzFevDxHhPNKhQl7AP6kQL
Content-Disposition: form-data; name="param2"
Content-Type: text/plain;charset=UTF-8
Content-Length: 14

value2
--Eh0oKOHPOSEIJTzFevDxHhPNKhQl7AP6kQL--

以下是代码:
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.MULTIPART_FORM_DATA);
MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
params.add("param1", "value1);
params.add("param2", "value2);
HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<>(params, headers);

URI uri = UriComponentsBuilder.fromHttpUrl("http://the-api:8080")
        .build().encode(Charset.forName("UTF-8")).toUri();

return restTemplate.postForObject(uri, request, KKPMailResponse.class);

问题

如何防止Spring的RestTemplate为每个参数自动添加头Content-Type: text/plain;charset=UTF-8Content-Length: xx

2个回答

1
如果您认为可以使用ClientHttpRequestInterceptor删除标头,则可以这样做:
public class SomeHttpRequestInterceptor implements ClientHttpRequestInterceptor
{

   @Override
   public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException
   {
        HttpHeaders headers = request.getHeaders();
        headers.remove("your header 1);
        headers.remove("your header 2);
        return execution.execute(request, body);
    }
}

以这种方式在RestTemplate中设置它:
RestTemplate restTemplate = new RestTemplate();
List<ClientHttpRequestInterceptor> interceptors = Arrays.asList(new CustomHttpRequestInterceptor())
restTemplate.setInterceptors(interceptors);

不幸的是,这将删除请求的主标头,而不是每个参数体中的标头。另一个问题是标头是不可变映射,remove()方法会抛出异常。 - asinkxcoswt

1

我没有找到阻止Spring生成这些条目的方法,但是您可以使用拦截器在发送请求之前将它们删除。为此,您需要在拦截器中按以下方式操作请求正文:

public class MultiPartFormDataCleaningInterceptor implements ClientHttpRequestInterceptor {

    @Override
    public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
        final MediaType contentType = request.getHeaders().getContentType();
        if (contentType != null
                && MediaType.MULTIPART_FORM_DATA.getType().equals(contentType.getType())
                && MediaType.MULTIPART_FORM_DATA.getSubtype().equals(contentType.getSubtype())) {
            return execution.execute(request, stripContentTypeAndLength(body));
        }
        return execution.execute(request, body);
    }

    private byte[] stripContentTypeAndLength(byte[] body) {
        final String bodyStr = new String(body);
        final StringBuilder builder = new StringBuilder();
        try (final Scanner scanner = new Scanner(bodyStr)) {
            while (scanner.hasNextLine()) {
                final String line = scanner.nextLine();
                if (!line.startsWith("Content-Type:")
                        && !line.startsWith("Content-Length:")) {
                    builder.append(line).append("\r\n");
                }
            }
        }
        final String newBodyStr = builder.toString();
        return newBodyStr.getBytes();
    }
}

这样不行,头部不是HTTP请求的“正文”部分。 - Michal
1
我们这里不是在谈论HTTP请求头。我们正在谈论多部分MIME头,它们在正文中。上面的代码片段有效(至少在我尝试时它运行良好)。 - Malte Finsterwalder

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