当出现RestClientException异常时,我该如何获取HTTP状态码和响应体?

50

RestTemplate 的方法(例如 postForEntity())会抛出 RestClientException。我想在 catch 块中从该异常对象中提取 HTTP 状态码和响应体。我应该怎么做?

4个回答

78

不要捕获 RestClientException,而是捕获特殊的 HttpClientErrorException

以下是一个示例:

try {
    Link dataCenterLink = serviceInstance.getLink("dataCenter");
    String dataCenterUrl = dataCenterLink.getHref();
    DataCenterResource dataCenter =
        restTemplate.getForObject(dataCenterUrl, DataCenterResource.class);
    serviceInstance.setDataCenter(dataCenter);
} catch (HttpClientErrorException e) {
    HttpStatus status = e.getStatusCode();
    if (status != HttpStatus.NOT_FOUND) { throw e; }
}

HttpClientErrorException 提供了 getStatusCodegetResponseBodyAsByteArray 方法,分别用于获取状态码和响应体。


1
更通用的一个是 HttpStatusCodeException,它被 HttpClientErrorExceptionHttpServerErrorException 继承。 - Dariush Jafari
5
如果你捕获到了 HttpStatusCodeException 异常,你就可以使用 getResponseBodyAsString() 方法来获取响应体的字符串形式。 - Nikhil Sahu
4
这个答案有什么问题吗?如果你调用的方法返回一个RestClientException,你不能简单地将其强制转换为HttpClientErrorException,对吧? - IcedDante
1
这个答案有风险,因为 HttpClientErrorException 只能捕获 4xx 状态码,但无法处理 5xx 状态码。应该使用 RestClientResponseException 或者添加 HttpServerErrorException 来处理 5xx 状态码。 - johnmweisz

10

应该捕获 RestClientResponseException 异常,因为它更加通用。

根据文档:这是一个包含实际 HTTP 响应数据的异常通用基类。


捕获异常是可行的,但我更喜欢使用HttpClientErrorException,因为你可以获取响应体以及完整的HttpStatus对象/枚举和相应的消息。例如:当使用另一个时,你可以获取“404”代码以及“未找到”描述。 - atom88
这是正确的答案,因为它处理的不仅仅是4xx代码。 - johnmweisz
同意,RestClientResponseException 包含响应头、响应体和状态码(HttpStatusCode 对象)。 - undefined

1
在某些情况下,不会抛出HttpClientErrorException。例如以下方法restTemplate.exchange的调用:
ResponseEntity<Employee[]> employees =  restTemplate.exchange(url, HttpMethod.GET, entity, Employee[].class);

获取HTTP主体并将其转换为实体。如果远程资源返回罕见错误,则内部转换不起作用,只会抛出RestClientException异常。

restTemplate.setErrorHandler

在这种情况下,或者如果你想要处理 restTemplate 操作中的任何错误,你可以使用 setErrorHandler。该方法接收一个基本的 ResponseErrorHandler,其中包含一些有用的方法。
该方法 hasError 允许我获取远程 http 正文文本,并帮助我检测调用或远程 http 资源中的错误:
restTemplate.setErrorHandler(new ResponseErrorHandler() {

  @Override
  public boolean hasError(ClientHttpResponse arg0) throws IOException {

    System.out.println("StatusCode from remote http resource:"+arg0.getStatusCode());
    System.out.println("RawStatusCode from remote http resource:"+arg0.getRawStatusCode());
    System.out.println("StatusText from remote http resource:"+arg0.getStatusText());

    String body = new BufferedReader(new InputStreamReader(arg0.getBody()))
          .lines().collect(Collectors.joining("\n"));

    System.out.println("Error body from remote http resource:"+body);
    return false;
  }

  @Override
  public void handleError(ClientHttpResponse arg0) throws IOException {
    // do something
  }
});

此外,您可以手动评估正文或状态,并返回true或false以标记为错误或非错误。

我的请求拦截器破坏了我的错误处理程序... - r brooks

-1
private void sendActivity(StatsActivity statsActivity) throws InterruptedException 
{
    LibraryConnectorXapiEditView libraryConnectorXapiEditView = (LibraryConnectorXapiEditView) workerBundle.getConnector();
    
    RestTemplate restTemplate = new RestTemplate();
    restTemplate.getMessageConverters().add(new MappingJackson2HttpMessageConverter());
    Statement statement = libraryConnectorConverter.convertActivityToStatement(statsActivity, workerBundle);
    HttpEntity<Statement> request = new HttpEntity<>(statement, headers);
    
    try
    {
       String lrsEndPoint = libraryConnectorXapiEditView.getLrsEndPoint() + "/statements";
       ResponseEntity<String> response = restTemplate.exchange(lrsEndPoint, HttpMethod.POST, request, String.class);
       ocnCompletionEventDao.save(this.convertToOcnCompletionEvent(statsActivity, response.getBody(), response.getStatusCodeValue()));
    }
    catch (HttpClientErrorException ex)
    {
      ocnCompletionEventDao.save(this.convertToOcnCompletionEvent(statsActivity, ex.getResponseBodyAsString(), ex.getStatusCode().value()));
      checkResponse(ex, libraryConnectorXapiEditView);  
      if(failedAttempts<3) 
      { 
          sendActivity(statsActivity);
          failedAttempts++;
      }
    }   
}

private void checkResponse(HttpClientErrorException ex, LibraryConnectorXapiEditView libraryConnectorXapiEditView) throws InterruptedException 
{
    int statusCode = ex.getStatusCode().value();
    int retryAfterSeconds = retryAfter(ex.getResponseHeaders());
    
    switch (statusCode)
    {
    case 401: 
        headers = xApiAuthorizationUtils.getHeaders(libraryConnectorXapiEditView);
    case 429:
        if(retryAfterSeconds!=0)
            Thread.sleep(retryAfterSeconds);
    case 422: 
        failedAttempts=3;
    }
 }

1
请不要仅仅回答代码,同时也要提供解释你的代码是如何解决问题的。带有解释的答案通常更有帮助、更具质量,并且更有可能吸引赞同。 - Pouria Hemi

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接