Spring Rest模板异常处理

dhxwm5r4  于 2022-09-18  发布在  Spring
关注(0)|答案(16)|浏览(258)

下面是代码片段;基本上,我试图在错误代码不是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中 Package REST模板交换方法吗?那么,代码的目的是什么?

mtb9vblg

mtb9vblg1#

您希望创建一个实现ResponseErrorHandler的类,然后使用它的一个示例来设置REST模板的错误处理:

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

此外,如果您只想覆盖handleError方法,则可以扩展类DefaultResponseErrorHandler而不是实现接口。

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

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

ih99xse1

ih99xse12#

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

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

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

np8igboo

np8igboo3#

您应该捕获HttpStatusCodeException异常:

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

5ssjco0h4#

另一个解决方案是本文末尾由“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 
}
1bqhqjot

1bqhqjot5#

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

在处理http响应时,您有许多类来Map最常见的情况。Http代码列表非常大,您不需要编写代码来处理每种情况。但是,例如,我们来看一下HttpClientErrorException子层次结构。您只有一个例外来Map任何4xx类型的错误。如果你需要深入研究,那你可以的。但是,只需捕获HttpClientErrorException,您就可以处理向服务提供坏数据的任何情况。

DefaultResponseErrorHandler非常简单和可靠。如果响应状态代码不是来自2xx家族,则它只为hasError方法返回TRUE。

41zrol4v

41zrol4v6#

我对此的处理如下:

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

rkue9o1l7#

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

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

e5nqia278#

如果您的RestTemplate使用池(http客户端工厂)或负载平衡(Eureka)机制,您将不会有机会为每个类创建一个new RestTemplate。如果您正在调用多个服务,则不能使用setErrorHandler,因为它将全局用于您的所有请求。

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

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

还有--但这是我自己的口味--我喜欢我的错误处理紧紧地依附在我的呼叫上。

nfg76nw0

nfg76nw09#

The code of exchange is below:

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

Exception RestClientException has HttpClientErrorException and HttpStatusCodeException exception.

So in RestTemplete there may occure HttpClientErrorException and HttpStatusCodeException exception. In exception object you can get exact error message using this way: exception.getResponseBodyAsString()

Here is the example code:

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

Here is the code description:

In this method you have to pass request and response class. This method will automatically parse response as requested object.

First of All you have to add message converter.

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

Then you have to add requestHeader. Here is the code:

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

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

Finally, you have to call exchange method:

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

For prety printing i used Gson library. here is the gradle : compile 'com.google.code.gson:gson:2.4'

You can just call the bellow code to get response:

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

Here is the full working code:

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

Thanks :)

lrl1mhuk

lrl1mhuk10#

要扩展@cararyt的回答,请稍加回答……

假设您的响应错误是由json消息返回的。例如,API可能返回204作为状态码错误,并返回json消息作为错误列表。在这种情况下,您需要定义哪些消息应该被视为错误,以及如何使用它们。

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

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

要在json以上消费并抛出自定义异常,可以执行以下操作:

首先定义一个用于Map错误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);
b91juud3

b91juud311#

这是如何处理REST模板中的异常

try {
        return restTemplate.exchange("URL", HttpMethod.POST, entity, String.class);
            }
        catch (HttpStatusCodeException e) 
        {
        return ResponseEntity.status(e.getRawStatusCode()).headers(e.getResponseHeaders())
                .body(e.getResponseBodyAsString());
        }
ztigrdn8

ztigrdn812#

下面是我使用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;
}
1u4esq0p

1u4esq0p13#

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

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

并且您需要为重模板Bean设置此处理程序:

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

c2e8gylq14#

在全局异常处理程序中阅读有关全局异常处理的内容添加下面的方法。这会奏效的。

@ExceptionHandler( {HttpClientErrorException.class, HttpStatusCodeException.class, HttpServerErrorException.class})
@ResponseBody
public ResponseEntity<Object> httpClientErrorException(HttpStatusCodeException e) throws IOException {
    BodyBuilder bodyBuilder = ResponseEntity.status(e.getRawStatusCode()).header("X-Backend-Status", String.valueOf(e.getRawStatusCode()));
    if (e.getResponseHeaders().getContentType() != null) {
        bodyBuilder.contentType(e.getResponseHeaders().getContentType());
    }
    return bodyBuilder.body(e.getResponseBodyAsString());
}
7vux5j2d

7vux5j2d15#

还可以选择使用TestRestTemplate。当您需要手动验证所有状态代码时(例如,在否定测试用例中),它对于集成和E2E测试非常有用。
TestRestTemplate具有容错能力。这意味着4xx和5xx不会导致抛出异常,而是可以通过响应实体及其状态代码进行检测。

相关问题