【问题标题】:org.springframework.web.reactive.function.UnsupportedMediaTypeException: Content type 'text/html;charset=iso-8859-1' not supported for bodyType=org.springframework.web.reactive.function.UnsupportedMediaTypeException:bodyType= 不支持内容类型'text/html;charset=iso-8859-1'
【发布时间】:2021-03-24 12:20:48
【问题描述】:

使用 Java 11,Springt Boot WebClient

创建了一个 REST Web 服务(服务 1),它尝试使用一个 REST Web 服务(服务 2),该服务预计会返回 JSON,该 JSON 将映射到我的自定义响应对象。但是由于要消费的服务(Service 2)目前不在线,我会收到 404 not found。

问题似乎是 404 响应以 'text/html;charset=iso-8859-1' 的形式出现,无法映射到我的 DocumentResponse 对象。

Webclient 配置为接受 xml 和 json。

如何拦截和处理这些 http 状态响应而不会出现此异常。

通常我会通过在我的请求链上使用 .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 一样简单,并且使用状态码我可以生成令人满意的响应消息。

但我发现一个奇怪的行为是,如果 Service 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。有人知道发生了什么吗?

【问题讨论】:

    标签: java spring-boot spring-webclient


    【解决方案1】:

    来自WebBlient#bodyToMonojava doc:

    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));
    

    如果您不希望默认错误处理程序返回 WebClientResponseException,请查看 WebClient.ResponseSpec#onStatus java doc 以及 Mono#onErrorResume 之类的内容。

    【讨论】:

    • 感谢您对本文档的引用。我通过在使用 .block() 并在那里处理错误的地方捕获 UnexpectedMediaTypeException 和 WebClientResponseException 来构建解决方法。当我这样做时,我发现如果服务 2 的响应是 f.e. 400 Bad Request 它以 text/html 形式交付,没有添加 charset=iso-8859-1。这会导致预期的 WebClientResponseException。但是对于 404 not found text/html charset=iso-8859-1 的实例被传递并导致了unexpectedMEdiaTypeException。有没有办法解释原因?
    【解决方案2】:

    回答我自己的问题:

    在预期的 404 状态代码的情况下,客户端给我 UnsupportedMediaTypeException 的奇怪行为是由重定向 307 后跟 302 引起的,这是由我使用 http url 而不是 https url 引起的。

    我不知道为什么它只是 404 状态码的情况。但是在将 baseURI 更改为 https 后,我不再收到重定向或 unsupportedMediaTypeExceptions,而是收到预期的 WEbClientResponseExceptions。

    现在我将尝试使用 Serg Vasylchak 的答案来处理错误而不捕获异常。

    【讨论】:

      猜你喜欢
      • 2019-11-23
      • 1970-01-01
      • 2023-02-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-09-18
      • 1970-01-01
      • 2012-11-02
      相关资源
      最近更新 更多