Spring Resttemplate异常处理


124

下面是代码片段;基本上,当错误代码不是200时,我正在尝试传播异常。

ResponseEntity<Object> response = restTemplate.exchange(url.toString().replace("{version}", version),
                    HttpMethod.POST, entity, Object.class);
            if(response.getStatusCode().value()!= 200){
                logger.debug("Encountered Error while Calling API");
                throw new ApplicationException();
            }

但是,如果来自服务器的响应为500,则会出现异常

org.springframework.web.client.HttpServerErrorException: 500 Internal Server Error
    at org.springframework.web.client.DefaultResponseErrorHandler.handleError(DefaultResponseErrorHandler.java:94) ~[spring-web-4.2.3.RELEASE.jar:4.2.3.RELEASE]

我真的需要在尝试中包装其余模板交换方法吗?那么代码的目的是什么?


请分享ApplicationException()的代码
Mudassar

Answers:


128

您想要创建一个实现的类,ResponseErrorHandler然后使用它的一个实例来设置其余模板的错误处理:

public class MyErrorHandler implements ResponseErrorHandler {
  @Override
  public void handleError(ClientHttpResponse response) throws IOException {
    // your error handling here
  }

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

[...]

public static void main(String args[]) {
  RestTemplate restTemplate = new RestTemplate();
  restTemplate.setErrorHandler(new MyErrorHandler());
}

而且,Spring仅提供了一个类DefaultResponseErrorHandler,您可以扩展该类而不是实现接口,以防万一您只想覆盖该handleError方法。

public class MyErrorHandler extends DefaultResponseErrorHandler {
  @Override
  public void handleError(ClientHttpResponse response) throws IOException {
    // your error handling here
  }
}

查看其源代码,以了解Spring如何处理HTTP错误。


1
我有1个RestTemplate实例,可以重复用于不同的调用。我需要以不同的方式处理来自不同调用的错误-显然无法使用全局处理程序来做到这一点-我需要为每个请求提供一个处理程序。
mvmn18年

4
使用此错误处理程序,我总会得到一个ResourceAccessException因为RestTemplate.doExecute捕获IOExceptions并引发一个ResourceAccessException。我究竟做错了什么?
Federico Bellucci

我可以通过扩展DefaultResponseErrorHandler来解决此问题。
Crenguta S '18 -10-23

48

您应该捕获一个HttpStatusCodeException例外:

try {
    restTemplate.exchange(...);
} catch (HttpStatusCodeException exception) {
    int statusCode = exception.getStatusCode().value();
    ...
}

37
IMO响应中应始终带有适当的状态代码,否则该代码的目的是什么。
vaibhav

5
我不确定是否理解@vaibhav反对意见:捕获HttpStatusCodeException不是针对错误的代码,而是因为在许多情况下始终会引发异常,因此无法执行if(code == value)。
Stefano Scarpanti

1
Java中的异常代价很高。偶尔出现意外原因(因此得名)是可以的,但是除此之外,您应该寻找其他解决方案。
Agoston Horvath

11
“非常昂贵”吗?比进行HTTP调用要贵吗?
IcedD​​ante

4
@RaffaelBecharaRameh-HttpStatusCodeException .getResponseBodyAsString()或HttpStatusCodeException.getResponseBodyAsByteArray()。
戴夫

45

Spring巧妙地将http错误代码视为异常,并假定您的异常处理代码具有处理错误的上下文。要使交换功能按预期运行,请执行以下操作:

    try {
        return restTemplate.exchange(url, httpMethod, httpEntity, String.class);
    } catch(HttpStatusCodeException e) {
        return ResponseEntity.status(e.getRawStatusCode()).headers(e.getResponseHeaders())
                .body(e.getResponseBodyAsString());
    }

这将从响应中返回所有预期结果。


2
您需要使用与默认SDK不同的HttpClient来获取错误的响应正文
razor

26

另一种解决方案是本文结尾处“ enlian”描述的解决方案:http ://springinpractice.com/2013/10/07/handling-json-error-object-responses-with-springs-resttemplate

try{
     restTemplate.exchange(...)
} catch(HttpStatusCodeException e){
     String errorpayload = e.getResponseBodyAsString();
     //do whatever you want
} catch(RestClientException e){
     //no response payload, tell the user sth else 
}

4
您需要使用与默认SDK不同的HttpClient,以获取错误的响应正文(例如apache commons HttpClient)
razor18年

17

Spring从非常大的http状态代码列表中抽象出您。那就是例外的想法。看一下org.springframework.web.client.RestClientException层次结构:

在处理http响应时,有一堆类可以映射最常见的情况。http代码列表确实很大,您不想编写代码来处理每种情况。但是,例如,看看HttpClientErrorException子层次结构。您只有一个例外来映射任何4xx类型的错误。如果您需要深入,那么可以。但是只要捕获HttpClientErrorException,就可以处理将错误数据提供给服务的任何情况。

DefaultResponseErrorHandler非常简单可靠。如果响应状态代码不是2xx系列的,则只对hasError方法返回true。


杜德,谢谢您的解释。您如何使用异常层次结构构建此树?
独立

1
嘿,我用过Eclipse。只需按Control + Shift + T打开类型搜索器,然后键入RestClientException。从结果中双击RestClientException,Eclipse将为您打开该类。然后,将鼠标光标放在类名称上(该名称显示为“ public class RestClientException ...”,然后按Control + T。您将看到该层次结构。)
Perimosh

你尝试了吗?
Perimosh

1
顺便说一句,这是Intellij:单击项目树中的类,然后按Ctrl + Alt + U,或单击鼠标右键->构建图
独立运行

3

如果将池(http客户端工厂)或负载平衡(eureka)机制与一起使用RestTemplate,则将无法创建new RestTemplate每个类。如果您要呼叫多个服务,那么您将无法使用该服务,setErrorHandler因为该服务将被全局用于所有请求。

在这种情况下,捕获HttpStatusCodeException似乎是更好的选择。

您唯一的其他选择是RestTemplate使用@Qualifier注释定义多个实例。

另外-但这是我自己的口味-我喜欢我的错误处理紧紧地贴在我的电话上。


3

我已经如下处理:

try {
  response = restTemplate.postForEntity(requestUrl, new HttpEntity<>(requestBody, headers), String.class);
} catch (HttpStatusCodeException ex) {
  response = new ResponseEntity<String>(ex.getResponseBodyAsString(), ex.getResponseHeaders(), ex.getStatusCode());
}

1

兑换代码如下

public <T> ResponseEntity<T> exchange(String url, HttpMethod method,
            HttpEntity<?> requestEntity, Class<T> responseType, Object... uriVariables) throws RestClientException

异常RestClientException具有HttpClientErrorExceptionHttpStatusCodeException异常。

因此,在RestTemplete有可能occure HttpClientErrorExceptionHttpStatusCodeException例外。在异常对象中,您可以使用以下方式获取确切的错误消息:exception.getResponseBodyAsString()

这是示例代码

public Object callToRestService(HttpMethod httpMethod, String url, Object requestObject, Class<?> responseObject) {

        printLog( "Url : " + url);
        printLog( "callToRestService Request : " + new GsonBuilder().setPrettyPrinting().create().toJson(requestObject));

        try {

            RestTemplate restTemplate = new RestTemplate();
            restTemplate.getMessageConverters().add(new MappingJackson2HttpMessageConverter());
            restTemplate.getMessageConverters().add(new StringHttpMessageConverter());


            HttpHeaders requestHeaders = new HttpHeaders();
            requestHeaders.setContentType(MediaType.APPLICATION_JSON);

            HttpEntity<Object> entity = new HttpEntity<>(requestObject, requestHeaders);

            long start = System.currentTimeMillis();

            ResponseEntity<?> responseEntity = restTemplate.exchange(url, httpMethod, entity, responseObject);

            printLog( "callToRestService Status : " + responseEntity.getStatusCodeValue());


            printLog( "callToRestService Body : " + new GsonBuilder().setPrettyPrinting().create().toJson(responseEntity.getBody()));

            long elapsedTime = System.currentTimeMillis() - start;
            printLog( "callToRestService Execution time: " + elapsedTime + " Milliseconds)");

            if (responseEntity.getStatusCodeValue() == 200 && responseEntity.getBody() != null) {
                return responseEntity.getBody();
            }

        } catch (HttpClientErrorException exception) {
            printLog( "callToRestService Error :" + exception.getResponseBodyAsString());
            //Handle exception here
        }catch (HttpStatusCodeException exception) {
            printLog( "callToRestService Error :" + exception.getResponseBodyAsString());
            //Handle exception here
        }
        return null;
    }

这是代码说明

在这种方法中,您必须传递请求和响应类。此方法将自动将响应解析为请求的对象。

首先,您必须添加消息转换器。

restTemplate.getMessageConverters().add(new MappingJackson2HttpMessageConverter());
            restTemplate.getMessageConverters().add(new StringHttpMessageConverter());

然后,您必须添加requestHeader。这是代码:

HttpHeaders requestHeaders = new HttpHeaders();
            requestHeaders.setContentType(MediaType.APPLICATION_JSON);

            HttpEntity<Object> entity = new HttpEntity<>(requestObject, requestHeaders);

最后,您必须调用交换方法:

ResponseEntity<?> responseEntity = restTemplate.exchange(url, httpMethod, entity, responseObject);

对于精美的打印,我使用了Gson库。这是gradle:compile 'com.google.code.gson:gson:2.4'

您只需调用以下代码即可获得响应:

ResponseObject response=new RestExample().callToRestService(HttpMethod.POST,"URL_HERE",new RequestObject(),ResponseObject.class);

这是完整的工作代码

import com.google.gson.GsonBuilder;
import org.springframework.http.*;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.web.client.HttpClientErrorException;
import org.springframework.web.client.HttpStatusCodeException;
import org.springframework.web.client.RestTemplate;


public class RestExample {

    public RestExample() {

    }

    public Object callToRestService(HttpMethod httpMethod, String url, Object requestObject, Class<?> responseObject) {

        printLog( "Url : " + url);
        printLog( "callToRestService Request : " + new GsonBuilder().setPrettyPrinting().create().toJson(requestObject));

        try {

            RestTemplate restTemplate = new RestTemplate();
            restTemplate.getMessageConverters().add(new MappingJackson2HttpMessageConverter());
            restTemplate.getMessageConverters().add(new StringHttpMessageConverter());


            HttpHeaders requestHeaders = new HttpHeaders();
            requestHeaders.setContentType(MediaType.APPLICATION_JSON);

            HttpEntity<Object> entity = new HttpEntity<>(requestObject, requestHeaders);

            long start = System.currentTimeMillis();

            ResponseEntity<?> responseEntity = restTemplate.exchange(url, httpMethod, entity, responseObject);

            printLog( "callToRestService Status : " + responseEntity.getStatusCodeValue());


            printLog( "callToRestService Body : " + new GsonBuilder().setPrettyPrinting().create().toJson(responseEntity.getBody()));

            long elapsedTime = System.currentTimeMillis() - start;
            printLog( "callToRestService Execution time: " + elapsedTime + " Milliseconds)");

            if (responseEntity.getStatusCodeValue() == 200 && responseEntity.getBody() != null) {
                return responseEntity.getBody();
            }

        } catch (HttpClientErrorException exception) {
            printLog( "callToRestService Error :" + exception.getResponseBodyAsString());
            //Handle exception here
        }catch (HttpStatusCodeException exception) {
            printLog( "callToRestService Error :" + exception.getResponseBodyAsString());
            //Handle exception here
        }
        return null;
    }

    private void printLog(String message){
        System.out.println(message);
    }
}

谢谢 :)


2
“ org.springframework.web.client.HttpClientErrorException”是“ org.springframework.web.client.HttpStatusCodeException”的子类。如果您已经在捕获HttpStatusCodeException并且在处理上述两个异常方面没有任何不同,则无需捕获HttpClientErrorException。
The-Proton-Resurgence

0

一个非常简单的解决方案可以是:

try {
     requestEntity = RequestEntity
     .get(new URI("user String"));
    
    return restTemplate.exchange(requestEntity, String.class);
} catch (RestClientResponseException e) {
        return ResponseEntity.status(e.getRawStatusCode()).body(e.getResponseBodyAsString());
}

-1

这是我的HTTPS POST方法,它为任何类型的错误响应返回响应正文。

public String postHTTPSRequest(String url,String requestJson)
{
    //SSL Context
    CloseableHttpClient httpClient = HttpClients.custom().setSSLHostnameVerifier(new NoopHostnameVerifier()).build();
    HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory();
    requestFactory.setHttpClient(httpClient);
    //Initiate REST Template
    RestTemplate restTemplate = new RestTemplate(requestFactory);
    HttpHeaders headers = new HttpHeaders();
    headers.setContentType(MediaType.APPLICATION_JSON);
    //Send the Request and get the response.
    HttpEntity<String> entity = new HttpEntity<String>(requestJson,headers);
    ResponseEntity<String> response;
    String stringResponse = "";
    try {
        response = restTemplate.postForEntity(url, entity, String.class);
        stringResponse = response.getBody();
    }
    catch (HttpClientErrorException e)
    {
        stringResponse = e.getResponseBodyAsString();
    }
    return stringResponse;
}

3
您需要使用与默认SDK不同的HttpClient,以获取错误的响应正文(例如apache commons HttpClient)
razor18年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.