当HTTP请求具有返回状态401时,如何在Java中解析响应主体
我正在使用Spring的RestTemplate
和Jackson来使用RESTful JSON API。 在某些情况下,我们可能会收到一个Status 401
(未授权)响应,其中包含由API制造商定义的自定义JSON主体,如下所示:
{ "code": 123, "message": "Reason for the error" }
我们需要解析主体,并在业务逻辑中使用code
属性。
这是我们需要解析的错误响应Java对象:
public class CustomError { @JsonProperty private Integer code; @JsonProperty private String message; public Integer getCode() { return code; } public String getMessage() { return message; } }
以及一个自定义error handling程序:
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); } } }
error handling程序因try块中的HttpMessageNotReadableException
而失败:
“无法读取JSON:由于服务器身份validation无法在流模式下重试”
这就是我发送请求的方式:
restTemplate.postForObject(url, pojoInstance, responseClass);
如果使用普通的旧rest客户端程序(如Postman)执行相同的请求,则会收到预期的JSON响应。 所以,我假设问题可能是Spring的ClientHttpResponse
实现以某种方式不允许访问响应体,如果是401状态。
是否确实可以解析响应主体?
更新
根据我的调查, RestTemplate
类使用ClientHttpResponse
,后者又创建了一个提供输入流的sun.net.www.protocol.http.HttpURLConnection
。 它就在那里,忽略输入流并抛出IOException
:
在流模式下,由于服务器身份validation无法重试
因此, HttpURLConnection
的实现导致了这个问题。
是否可以避免这个问题? 也许我们应该使用一个替代实现,在发生错误状态代码时不会忽略响应主体? 你能推荐任何替代品吗?
无需自定义处理程序即可尝试以下方法。 我们的想法是将响应作为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); } }
更新:使用其他工厂也可能有所帮助,因为与您的问题相关的默认错误中存在错误(请参阅下面的评论):
RestTemplate template = new RestTemplate(new HttpComponentsClientHttpRequestFactory());