org.springframework.web.reactive.function.UnsupportedMediaTypeException: bodyType= 不支持内容类型 'text/html;charset=iso-8859-1'
org.springframework.web.reactive.function.UnsupportedMediaTypeException: Content type 'text/html;charset=iso-8859-1' not supported for bodyType=
使用Java11、Springt Boot WebClient
创建了一个 REST Web 服务(服务 1),它尝试使用一个 REST Web 服务(服务 2),预计 return JSON 将映射到我的自定义响应对象。但是因为要消费的服务(服务2)暂时不在线我会收到404 not found.
问题似乎是 404 响应以 'text/html;charset=iso-8859-1' 的形式出现,无法映射到我的 DocumentResponse 对象上。
Web 客户端配置为接受 xml 和 json。
如何拦截和处理那些 http 状态响应而不会出现此异常。
通常我会在我的请求链上收到 using.block() 的响应,并为正在使用该响应的服务 1 创建适当的响应。
例如:
Documentresponse response = invoiceDeliveryControllerApi.deliverInvoiceUsingPOST(request,"test").block();
public Mono<Documentresponse> deliverInvoiceUsingPOST(Documentrequest request, String name) throws WebClientResponseException {
Object postBody = request;
// verify the required parameter 'request' is set
if (request == null) {
throw new WebClientResponseException("Missing the required parameter 'request' when calling deliverInvoiceUsingPOST", HttpStatus.BAD_REQUEST.value(), HttpStatus.BAD_REQUEST.getReasonPhrase(), null, null, null);
}
// create path and map variables
final Map<String, Object> pathParams = new HashMap<String, Object>();
final MultiValueMap<String, String> queryParams = new LinkedMultiValueMap<String, String>();
final HttpHeaders headerParams = new HttpHeaders();
final MultiValueMap<String, String> cookieParams = new LinkedMultiValueMap<String, String>();
final MultiValueMap<String, Object> formParams = new LinkedMultiValueMap<String, Object>();
queryParams.putAll(apiClient.parameterToMultiValueMap(null, "name", name));
final String[] localVarAccepts = {
"application/xml", "text/xml"
};
final List<MediaType> localVarAccept = apiClient.selectHeaderAccept(localVarAccepts);
final String[] localVarContentTypes = {
"application/xml", "text/xml"
};
final MediaType localVarContentType = apiClient.selectHeaderContentType(localVarContentTypes);
String[] localVarAuthNames = new String[] { };
ParameterizedTypeReference<Documentresponse> localVarReturnType = new ParameterizedTypeReference<Documentresponse>() {};
return apiClient.invokeAPI("/api/v1/deliverinvoice", HttpMethod.POST, pathParams, queryParams, postBody, headerParams, cookieParams, formParams, localVarAccept, localVarContentType, localVarAuthNames, localVarReturnType);
}
public <T> Mono<T> invokeAPI(String path, HttpMethod method, Map<String, Object> pathParams, MultiValueMap<String, String> queryParams, Object body, HttpHeaders headerParams, MultiValueMap<String, String> cookieParams, MultiValueMap<String, Object> formParams, List<MediaType> accept, MediaType contentType, String[] authNames, ParameterizedTypeReference<T> returnType) throws RestClientException {
final WebClient.RequestBodySpec requestBuilder = prepareRequest(path, method, pathParams, queryParams, body, headerParams, cookieParams, formParams, accept, contentType, authNames);
return requestBuilder.retrieve().bodyToMono(returnType);
}
public static WebClient buildWebClient(ObjectMapper mapper) {
ExchangeStrategies strategies = ExchangeStrategies
.builder()
.codecs(clientDefaultCodecsConfigurer -> {
clientDefaultCodecsConfigurer.defaultCodecs().jackson2JsonEncoder(new Jackson2JsonEncoder(mapper, MediaType.APPLICATION_JSON));
clientDefaultCodecsConfigurer.defaultCodecs().jackson2JsonDecoder(new Jackson2JsonDecoder(mapper, MediaType.APPLICATION_JSON));
}).build();
WebClient.Builder webClient = WebClient.builder().filters(exchangeFilterFunctions -> {
exchangeFilterFunctions.add(logRequest());
exchangeFilterFunctions.add(logResponse());
}).exchangeStrategies(strategies);
return webClient.build();
}
现在我想找到一种方法来对那些 html 响应做出反应,并为服务 1 创建一个响应,告诉用户在调用服务 2 时出现了 http 连接错误
有什么建议吗?
PS:客户端代码是使用 openapi 代码生成器生成的,并针对日志目的进行了微调。
编辑:我发现通常它就像在 .block() 中捕获 WebClientException 一样简单,并且使用状态代码我可以生成令人满意的响应消息。
但是我发现一个奇怪的行为是,如果服务 2 的响应是 f.e。 400 Bad Request 它作为 text/html 交付,没有添加 charset=iso-8859-1。
在这种情况下,它会导致预期的 WebClientResponseException。但是对于 404 not found 的实例 text/html charset=iso-8859-1 被传递并导致 UnexpectedMediaTypeException。有没有办法解释为什么?
更令我困惑的是,在预期的 404 情况下,我通过 WebClientFilters 进行的日志记录提供了状态 307 重定向(到我首先调用的相同 url)和 302 发现 text/html charset=iso-8859-1 也导致 MediaTypeException。虽然通过 Postman 使用完全相同的数据进行相同的调用,但发送 404。有人知道发生了什么吗?
来自 WebBlient#bodyToMono
java 文档:
Extract the body to a {@code Mono}. By default, if the response has status code 4xx or 5xx, the {@code Mono} will contain a {@link WebClientException}. This can be overridden with {@link #onStatus(Predicate, Function)}.
根据状态代码实现错误处理:
requestBuilder
.retrieve()
.onStatus(HttpStatus::is4xxClientError, res -> Mono<? extends Throwable>)
.onStatus(HttpStatus::is5xxServerError, res -> Mono<? extends Throwable>)
.bodyToMono(responseType)
.onErrorResume(Status400Throwable.class, th -> Mono.just(fallbackResponseHere))
.onErrorResume(Status500Throwable.class, th -> Mono.just(fallbackResponseHere));
查看 WebClient.ResponseSpec#onStatus
java 文档以及类似 Mono#onErrorResume
的内容,以防您不希望默认错误处理程序 return WebClientResponseException
.
回答我自己的问题:
在预期的 404 状态代码的情况下,客户端给我 UnsupportedMediaTypeException 的奇怪行为不知何故是由重定向 307 后跟 302 found 引起的,这是由我使用 http url 而不是 https url.
我不知道为什么只有 404 状态代码才会出现这种情况。但是在将 baseURI 更改为 https 之后,我不再收到重定向或不支持的媒体类型异常,而是收到预期的 WEbClientResponseExceptions。
现在我将尝试使用 Serg Vasylchak 的答案来处理错误而不捕获异常。
使用Java11、Springt Boot WebClient
创建了一个 REST Web 服务(服务 1),它尝试使用一个 REST Web 服务(服务 2),预计 return JSON 将映射到我的自定义响应对象。但是因为要消费的服务(服务2)暂时不在线我会收到404 not found.
问题似乎是 404 响应以 'text/html;charset=iso-8859-1' 的形式出现,无法映射到我的 DocumentResponse 对象上。
Web 客户端配置为接受 xml 和 json。
如何拦截和处理那些 http 状态响应而不会出现此异常。
通常我会在我的请求链上收到 using.block() 的响应,并为正在使用该响应的服务 1 创建适当的响应。
例如:
Documentresponse response = invoiceDeliveryControllerApi.deliverInvoiceUsingPOST(request,"test").block();
public Mono<Documentresponse> deliverInvoiceUsingPOST(Documentrequest request, String name) throws WebClientResponseException {
Object postBody = request;
// verify the required parameter 'request' is set
if (request == null) {
throw new WebClientResponseException("Missing the required parameter 'request' when calling deliverInvoiceUsingPOST", HttpStatus.BAD_REQUEST.value(), HttpStatus.BAD_REQUEST.getReasonPhrase(), null, null, null);
}
// create path and map variables
final Map<String, Object> pathParams = new HashMap<String, Object>();
final MultiValueMap<String, String> queryParams = new LinkedMultiValueMap<String, String>();
final HttpHeaders headerParams = new HttpHeaders();
final MultiValueMap<String, String> cookieParams = new LinkedMultiValueMap<String, String>();
final MultiValueMap<String, Object> formParams = new LinkedMultiValueMap<String, Object>();
queryParams.putAll(apiClient.parameterToMultiValueMap(null, "name", name));
final String[] localVarAccepts = {
"application/xml", "text/xml"
};
final List<MediaType> localVarAccept = apiClient.selectHeaderAccept(localVarAccepts);
final String[] localVarContentTypes = {
"application/xml", "text/xml"
};
final MediaType localVarContentType = apiClient.selectHeaderContentType(localVarContentTypes);
String[] localVarAuthNames = new String[] { };
ParameterizedTypeReference<Documentresponse> localVarReturnType = new ParameterizedTypeReference<Documentresponse>() {};
return apiClient.invokeAPI("/api/v1/deliverinvoice", HttpMethod.POST, pathParams, queryParams, postBody, headerParams, cookieParams, formParams, localVarAccept, localVarContentType, localVarAuthNames, localVarReturnType);
}
public <T> Mono<T> invokeAPI(String path, HttpMethod method, Map<String, Object> pathParams, MultiValueMap<String, String> queryParams, Object body, HttpHeaders headerParams, MultiValueMap<String, String> cookieParams, MultiValueMap<String, Object> formParams, List<MediaType> accept, MediaType contentType, String[] authNames, ParameterizedTypeReference<T> returnType) throws RestClientException {
final WebClient.RequestBodySpec requestBuilder = prepareRequest(path, method, pathParams, queryParams, body, headerParams, cookieParams, formParams, accept, contentType, authNames);
return requestBuilder.retrieve().bodyToMono(returnType);
}
public static WebClient buildWebClient(ObjectMapper mapper) {
ExchangeStrategies strategies = ExchangeStrategies
.builder()
.codecs(clientDefaultCodecsConfigurer -> {
clientDefaultCodecsConfigurer.defaultCodecs().jackson2JsonEncoder(new Jackson2JsonEncoder(mapper, MediaType.APPLICATION_JSON));
clientDefaultCodecsConfigurer.defaultCodecs().jackson2JsonDecoder(new Jackson2JsonDecoder(mapper, MediaType.APPLICATION_JSON));
}).build();
WebClient.Builder webClient = WebClient.builder().filters(exchangeFilterFunctions -> {
exchangeFilterFunctions.add(logRequest());
exchangeFilterFunctions.add(logResponse());
}).exchangeStrategies(strategies);
return webClient.build();
}
现在我想找到一种方法来对那些 html 响应做出反应,并为服务 1 创建一个响应,告诉用户在调用服务 2 时出现了 http 连接错误
有什么建议吗?
PS:客户端代码是使用 openapi 代码生成器生成的,并针对日志目的进行了微调。
编辑:我发现通常它就像在 .block() 中捕获 WebClientException 一样简单,并且使用状态代码我可以生成令人满意的响应消息。
但是我发现一个奇怪的行为是,如果服务 2 的响应是 f.e。 400 Bad Request 它作为 text/html 交付,没有添加 charset=iso-8859-1。
在这种情况下,它会导致预期的 WebClientResponseException。但是对于 404 not found 的实例 text/html charset=iso-8859-1 被传递并导致 UnexpectedMediaTypeException。有没有办法解释为什么?
更令我困惑的是,在预期的 404 情况下,我通过 WebClientFilters 进行的日志记录提供了状态 307 重定向(到我首先调用的相同 url)和 302 发现 text/html charset=iso-8859-1 也导致 MediaTypeException。虽然通过 Postman 使用完全相同的数据进行相同的调用,但发送 404。有人知道发生了什么吗?
来自 WebBlient#bodyToMono
java 文档:
Extract the body to a {@code Mono}. By default, if the response has status code 4xx or 5xx, the {@code Mono} will contain a {@link WebClientException}. This can be overridden with {@link #onStatus(Predicate, Function)}.
根据状态代码实现错误处理:
requestBuilder
.retrieve()
.onStatus(HttpStatus::is4xxClientError, res -> Mono<? extends Throwable>)
.onStatus(HttpStatus::is5xxServerError, res -> Mono<? extends Throwable>)
.bodyToMono(responseType)
.onErrorResume(Status400Throwable.class, th -> Mono.just(fallbackResponseHere))
.onErrorResume(Status500Throwable.class, th -> Mono.just(fallbackResponseHere));
查看 WebClient.ResponseSpec#onStatus
java 文档以及类似 Mono#onErrorResume
的内容,以防您不希望默认错误处理程序 return WebClientResponseException
.
回答我自己的问题:
在预期的 404 状态代码的情况下,客户端给我 UnsupportedMediaTypeException 的奇怪行为不知何故是由重定向 307 后跟 302 found 引起的,这是由我使用 http url 而不是 https url.
我不知道为什么只有 404 状态代码才会出现这种情况。但是在将 baseURI 更改为 https 之后,我不再收到重定向或不支持的媒体类型异常,而是收到预期的 WEbClientResponseExceptions。
现在我将尝试使用 Serg Vasylchak 的答案来处理错误而不捕获异常。