来自 POST 的 415 不支持的媒体类型

415 Unsupported Media Type from POST

我需要使用 Spring/Webclient 创建一个 POST 调用,它可以很好地与 curl 一起工作。

POSTDATA="{\"subject\":\"CN=test\",\"type\": \"ssh\",\"usage\":\"login\",\"validityPeriod\":7300,\"account\":\"1234\"}"

SSHKEY="ssh-rsa ..."

http_status=$(curl -k \
    -u <user>:<password>\
    -X POST "xyz" \
    -H "Accept: application/json" \
    -H "Content-Type: multipart/mixed" \
    -F "datapart=$POSTDATA;type=application/json" \
    -F "certipart=$SSHKEY2;type=application/octet-stream" )

echo "https status $http_status \n"

上面的 curl 命令工作正常(在 bash 脚本中)。

使用下面的代码我总是得到一个异常。此代码从 Angular 前端调用,主体为 ssh-rsa-Key。

@PostMapping(value = "/v1/authentication/saveNewKey", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
    public Certificate saveNewKey(@RequestBody String newKey) throws JsonProcessingException {

        Certificate c = new Certificate();
        c.setSubject("CN=test");
        c.setType("ssh");
        c.setUsage("login");
        c.setValidityPeriod(7300);
        c.setAccount("1234");

        ObjectMapper mapper = new ObjectMapper();
        MultipartBodyBuilder bodyBuilder = new MultipartBodyBuilder();

        bodyBuilder.part("datapart", mapper.writeValueAsString(c), MediaType.APPLICATION_JSON);
        bodyBuilder.part("certipart", newKey, MediaType.APPLICATION_OCTET_STREAM);

        Mono<Certificate> response = webClientBuilder.build()
                .post()
                .uri(apiBaseUrl + CERTIFICATES_API_URL)
                .contentType(MediaType.MULTIPART_MIXED)
                .body(BodyInserters.fromMultipartData(bodyBuilder.build()))
                .headers(headers -> {
                    headers.setBasicAuth(basicAuthUser, basicAuthPwd);
                    headers.setAccept(Arrays.asList(MediaType.APPLICATION_JSON));
                    headers.setContentType(MediaType.MULTIPART_MIXED);
                })
                .retrieve()
                .bodyToMono(Certificate.class)
                .log();

        return response.block();
}
org.springframework.web.reactive.function.client.WebClientResponseException$UnsupportedMediaType: 415 Unsupported Media Type from POST xyz
        at org.springframework.web.reactive.function.client.WebClientResponseException.create(WebClientResponseException.java:212)
        Suppressed: reactor.core.publisher.FluxOnAssembly$OnAssemblyException:
Error has been observed at the following site(s):
        *__checkpoint ? 415 from POST xyz [DefaultWebClient]
Original Stack Trace:
                at org.springframework.web.reactive.function.client.WebClientResponseException.create(WebClientResponseException.java:212)
...

我错过了什么?

我能够解决我的问题。

如果使用 BodyInserters.fromMultipartData 添加正文,则 ContentType 将始终为 multipart/form-data。我的 API.

不允许使用此 ContentType
.body(BodyInserters.fromMultipartData(bodyBuilder.build()))

解决方法: 在 webClientbuilder

之外构建主体
MultiValueMap<String, HttpEntity<?>> body = bodyBuilder.build();

并将此正文添加到 bodyValue(不是 .body()!)

.bodyValue(body)

这将导致 multipart/mixed 的 ContentType(如果像我的问题一样正确设置)。