Spring Resttemplate异常处理

时间:2016-06-29 07:46:57

标签: spring rest exception-handling resttemplate

以下是代码段;基本上,我试图在错误代码不是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]

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

13 个答案:

答案 0 :(得分:90)

您想创建一个实现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
  }
}

看看它的source code,了解Spring如何处理HTTP错误。

答案 1 :(得分:26)

你应该抓住HttpStatusCodeException例外:

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

答案 2 :(得分:17)

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());
    }

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

答案 3 :(得分:8)

另一种解决方案是本文末尾所描述的&#34; enlian&#34;: 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 :(得分:5)

这些教程或其他教程都没有为我工作。 RestTemplate简直就是废话。我在写这篇文章是为了让其他人不要在这个可怕的事情上浪费时间。 我调用restTemplate.exchange并获取400状态代码。该行还会引发异常,但这不是HttpStatusCodeException,而是ResourceAccessException。我试过交换对象和字符串。我尝试过包括ResponseErrorHandler,这是完全没有用的。

这不是我第一次也无法使用RestTemplate做我想做的事情,而且我甚至很长时间都没有使用它。不要浪费时间。

例如,您可以使用:

Apache HttpClient: https://mvnrepository.com/artifact/org.apache.httpcomponents/httpclient

或 OkHttp3: https://mvnrepository.com/artifact/com.squareup.okhttp3/okhttp

答案 5 :(得分:5)

我已按以下方式处理此问题:

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

答案 6 :(得分:1)

如果您对RestTemplate使用池化(http客户端工厂)或负载平衡(尤里卡)机制,则无法为每个类创建new RestTemplate。如果您正在呼叫多个服务,则无法使用setErrorHandler,因为如果全局用于您的所有请求。

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

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

另外 - 但这是我自己的品味 - 我喜欢我的错误处理紧紧依偎在我的电话中。

答案 7 :(得分:1)

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

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

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

答案 8 :(得分:1)

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

.as-console-wrapper { max-height: 100% !important; top: 0; }

答案 9 :(得分:0)

交易代码如下

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

异常RestClientException具有HttpClientErrorExceptionHttpStatusCodeException异常。

因此,在RestTemplete中可能发生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);
    }
}

谢谢:)

答案 10 :(得分:0)

我通过覆盖 DefaultResponseErrorHandler 类中的 hasError 方法修复了它:

public class BadRequestSafeRestTemplateErrorHandler extends DefaultResponseErrorHandler
{
    @Override
    protected boolean hasError(HttpStatus statusCode)
    {
        if(statusCode == HttpStatus.BAD_REQUEST)
        {
            return false;
        }
        return statusCode.isError();
    }
}

并且您需要为 restemplate bean 设置此处理程序:

@Bean
    protected RestTemplate restTemplate(RestTemplateBuilder builder)
    {
        return builder.errorHandler(new BadRequestSafeRestTemplateErrorHandler()).build();
    }

答案 11 :(得分:0)

稍微延长@carcaret 的回答....

考虑到您的响应错误是由 json 消息返回的。例如,API 可能会返回 204 作为状态代码错误和 json 消息作为错误列表。在这种情况下,您需要定义 spring 应将哪些消息视为错误以及如何使用它们。

作为示例,如果发生错误,您的 API 可能会返回如下内容:

 { "errorCode":"TSC100" , "errorMessage":"The foo bar error happend" , "requestTime" : "202112827733" .... } 

要使用上面的json并抛出自定义异常,您可以执行以下操作:

首先定义一个类来映射错误ro对象

//just to map the json to object
public class ServiceErrorResponse implements Serializable {

    //setter and getters
    private Object errorMessage;
    private String errorCode;
    private String requestTime;
   
}

现在定义错误处理程序:

public class ServiceResponseErrorHandler implements ResponseErrorHandler {

    private List<HttpMessageConverter<?>> messageConverters;

    @Override
    public boolean hasError(ClientHttpResponse response) throws IOException {
        
        return (response.getStatusCode().is4xxClientError() ||
                response.getStatusCode().is5xxServerError());
    }

    @Override
    public void handleError(ClientHttpResponse response) throws IOException {
        
        @SuppressWarnings({ "unchecked", "rawtypes" })
        HttpMessageConverterExtractor<ServiceErrorResponse> errorMessageExtractor = 
                new HttpMessageConverterExtractor(ServiceErrorResponse.class, messageConverters);
        
        ServiceErrorResponse errorObject = errorMessageExtractor.extractData(response);
        
       throw new ResponseEntityErrorException(
               ResponseEntity.status(response.getRawStatusCode())
                                .headers(response.getHeaders())
                                .body(errorObject)
               );
        
    }

    public void setMessageConverters(List<HttpMessageConverter<?>> messageConverters) {
        this.messageConverters = messageConverters;
    }
}

自定义异常将是:

public class ResponseEntityErrorException extends RuntimeException  {
    
    private ResponseEntity<ServiceErrorResponse> serviceErrorResponseResponse;

    public ResponseEntityErrorException(ResponseEntity<ServiceErrorResponse> serviceErrorResponseResponse) {
        this.serviceErrorResponseResponse = serviceErrorResponseResponse;
    }
    
    public ResponseEntity<ServiceErrorResponse> getServiceErrorResponseResponse() {
        return serviceErrorResponseResponse;
    }
}

使用:

RestTemplateResponseErrorHandler errorHandler = new 
RestTemplateResponseErrorHandler();
//pass the messageConverters to errror handler and let it convert json to object
        errorHandler.setMessageConverters(restTemplate.getMessageConverters());
        restTemplate.setErrorHandler(errorHandler);

答案 12 :(得分:-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;
}