当 HTTP 请求返回状态为 401 时,如何在 Java 中解析响应正文

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/26676405/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-11 03:02:28  来源:igfitidea点击:

How to parse the response body in Java, when the HTTP request has return status 401

javaspringapiHymansonresttemplate

提问by Ivaylo Slavov

I am consuming a RESTful JSON API using Spring's RestTemplateand Hymanson. In some cases we may receive a Status 401(Unauthorized) response with a custom JSON body, that is defined by the API manufacturer, and looks like this:

我正在使用 SpringRestTemplate和 Hymanson使用 RESTful JSON API 。在某些情况下,我们可能会收到Status 401带有自定义 JSON 主体的(未授权)响应,该主体由 API 制造商定义,如下所示:

{
    "code": 123,
    "message": "Reason for the error"
}

We need to parse the body, and use the codeproperty in our business logic.

我们需要解析正文,并code在我们的业务逻辑中使用该属性。

This is the error response Java object we need to parse to:

这是我们需要解析为的错误响应 Java 对象:

public class CustomError {

    @JsonProperty
    private Integer code;
    @JsonProperty
    private String message;

    public Integer getCode() {
       return code;
    }
    public String getMessage() {
        return message;
    }
}

And a custom error handler to do this:

和一个自定义错误处理程序来做到这一点:

public class CustomErrorHandler extends DefaultResponseErrorHandler {
    private RestTemplate restTemplate;
    private ObjectMapper objectMapper;
    private MappingHymansonHttpMessageConverter messageConverter;


    @Override
    public boolean hasError(ClientHttpResponse response) throws IOException {
        return super.hasError(response);
    }

    @Override
    public void handleError(final ClientHttpResponse response) throws IOException {

        try {
            CustomError error = 
                (CustomError) messageConverter.read(CustomError.class, response);
            throw new CustomErrorIOException(error, error.getMessage());
        } catch (Exception e) {
            // parsing failed, resort to default behavior
            super.handleError(response);
        }
    }
}

The error handler fails with an HttpMessageNotReadableExceptionin the try block:

错误处理程序失败并HttpMessageNotReadableException在 try 块中出现:

"Could not read JSON: cannot retry due to server authentication, in streaming mode"

“无法读取 JSON:由于服务器身份验证,无法重试,在流模式下”

This is how I am sending requests:

这是我发送请求的方式:

restTemplate.postForObject(url, pojoInstance, responseClass);

If the same request is executed with a plain old rest client program, like Postman, the expected JSON response is received. So, I assume the problem could be with the Spring's ClientHttpResponseimplementation somehow not allowing access to the response body, in case of the 401 status.

如果使用普通的老式休息客户端程序(如 Postman)执行相同的请求,则会收到预期的 JSON 响应。因此,我认为问题可能在于 Spring 的ClientHttpResponse实现以某种方式不允许访问响应正文,以防出现 401 状态。

Is it indeed possible to parse the response body?

真的可以解析响应体吗?

Update

更新

From what I investigated, the RestTemplateclass uses ClientHttpResponsewhich in turn creates an sun.net.www.protocol.http.HttpURLConnectionthat provides the input stream. It is there, where the input stream is being neglected and an IOExceptionis thrown:

根据我的调查,RestTemplate该类使用ClientHttpResponse它反过来创建一个sun.net.www.protocol.http.HttpURLConnection提供输入流的。在那里,输入流被忽略并被IOException抛出:

cannot retry due to server authentication, in streaming mode

由于服务器身份验证无法重试,在流模式下

So, the HttpURLConnection's implementation is causing the issue.

因此,HttpURLConnection的实现导致了这个问题。

Will it be possible to avoid this problem? Perhaps we should use an alternative implementation that does not ignore the response body in case of an error status code? Can you recommend any alternatives?

有可能避免这个问题吗?也许我们应该使用一种替代实现,在出现错误状态代码时不会忽略响应主体?你能推荐任何替代品吗?

采纳答案by Marios

Try the following approach without needing a custom handler. The idea is to get the response as a string from the HttpStatusCodeException, and then you can convert it to your object. For the conversion I used the Hymanson's ObjectMapper:

无需自定义处理程序即可尝试以下方法。这个想法是从 HttpStatusCodeException 以字符串形式获取响应,然后您可以将其转换为您的对象。对于转换,我使用了 Hymanson 的 ObjectMapper:

        try {

            restTemplate.postForObject(url, pojoInstance, responseClass);

        } catch (HttpStatusCodeException e) {

            if (e.getStatusCode() == HttpStatus.UNAUTHORIZED) {

                String responseString = e.getResponseBodyAsString();

                ObjectMapper mapper = new ObjectMapper();

                CustomError result = mapper.readValue(responseString,
                        CustomError.class);
            }
        }

Update:Usage of a different factory may also help since there is a bug in the default one related to your issue (see comment below):

更新:使用不同的工厂也可能有所帮助,因为与您的问题相关的默认工厂存在错误(请参阅下面的评论):

RestTemplate template = new RestTemplate(new HttpComponentsClientHttpRequestFactory());