当HTTP请求具有返回状态401时,如何在Java中解析响应主体

Iva*_*vov 21 java api spring jackson resttemplate

我正在使用Spring RestTemplate和Jackson 来使用RESTful JSON API .在某些情况下,我们可能会收到一个Status 401(未经授权的)响应,其中包含由API制造商定义的自定义JSON主体,如下所示:

{
    "code": 123,
    "message": "Reason for the error"
}
Run Code Online (Sandbox Code Playgroud)

我们需要解析主体,并code在业务逻辑中使用该属性.

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

public class CustomError {

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

    public Integer getCode() {
       return code;
    }
    public String getMessage() {
        return message;
    }
}
Run Code Online (Sandbox Code Playgroud)

和自定义错误处理程序执行此操作:

public class CustomErrorHandler extends DefaultResponseErrorHandler {
    private RestTemplate restTemplate;
    private ObjectMapper objectMapper;
    private MappingJacksonHttpMessageConverter 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);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

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

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

这就是我发送请求的方式:

restTemplate.postForObject(url, pojoInstance, responseClass);
Run Code Online (Sandbox Code Playgroud)

如果使用普通的旧休息客户端程序(如Postman)执行相同的请求,则会收到预期的JSON响应.所以,我认为问题可能是Spring的ClientHttpResponse实现以某种方式不允许访问响应体,如果是401状态.

是否确实可以解析响应主体?

更新

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

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

因此,该HttpURLConnection实施正在引发​​这个问题.

是否可以避免这个问题?也许我们应该使用一个替代实现,在错误状态代码的情况下不会忽略响应主体?你能推荐任何替代品吗?

Mar*_*ios 40

无需自定义处理程序即可尝试以下方法.我们的想法是将响应作为HttpStatusCodeException中的字符串获取,然后您可以将其转换为您的对象.对于转换,我使用了Jackson的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);
            }
        }
Run Code Online (Sandbox Code Playgroud)

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

RestTemplate template = new RestTemplate(new HttpComponentsClientHttpRequestFactory());
Run Code Online (Sandbox Code Playgroud)

  • 我试过这个解决方案,但似乎没有用.实际抛出异常,但响应流不包含任何数据.如果我用POSTMAN尝试相同的请求,响应主体包含正文,所以它似乎再次出现RestTemplate问题. (7认同)
  • 我发现了一个描述您在https://jira.spring.io/browse/SPR-9999中遇到的问题的错误.这是从去年开始,但也许尚未确定.它建议使用不同的工厂,以便使用另一个客户端实现(RestTemplate template = new RestTemplate(new HttpComponentsClientHttpRequestFactory()); (4认同)
  • Ivaylo,确实如此,SimpleClientHttpConnectionFactory使用java.net实现进行HTTP请求,而HttpComponentsClientHttpRequestFactory使用Apache的HttpClient下面 (3认同)
  • @Marios,感谢您的努力。我之前使用过`SimpleClientHttpConnectionFactory`。您的解决方案在使用 `HttpComponentsClientHttpRequestFactory` 时对我有用 - 也许上面提到的 `sun.net.www.protocol.http.HttpURLConnection` 没有被它使用。 (2认同)
  • 请注意:HttpStatusCodeException 仅提供有关 HTTP 状态代码的语法糖,但是您在 RestClientResponseException 中拥有“getRawStatusCode()”和“getResponseBodyAsString()”所需的内容。 (2认同)