Xử lý ngoại lệ Spring Resttemplate


124

Dưới đây là đoạn mã; về cơ bản, tôi đang cố gắng tuyên truyền ngoại lệ khi mã lỗi là bất kỳ thứ gì khác hơn 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();
            }

Tuy nhiên, trong trường hợp có 500 phản hồi từ máy chủ, tôi nhận được ngoại lệ

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]

Tôi có thực sự cần thử phương pháp trao đổi mẫu còn lại không? Mục đích của mã sau đó là gì?


Vui lòng chia sẻ mã của ApplicationException ()
Mudassar

Câu trả lời:


128

Bạn muốn tạo một lớp triển khai ResponseErrorHandlervà sau đó sử dụng một phiên bản của nó để đặt việc xử lý lỗi cho mẫu phần còn lại của bạn:

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

Ngoài ra, Spring có lớp DefaultResponseErrorHandlermà bạn có thể mở rộng thay vì triển khai giao diện, trong trường hợp bạn chỉ muốn ghi đè handleErrorphương thức.

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

Hãy xem mã nguồn của nó để có ý tưởng về cách Spring xử lý lỗi HTTP.


1
Tôi có 1 phiên bản RestTemplate mà tôi sử dụng lại cho các cuộc gọi khác nhau. Tôi cần xử lý các lỗi từ các cuộc gọi khác nhau theo cách khác nhau - rõ ràng là không có cách nào để làm điều đó với trình xử lý toàn cầu - tôi cần cung cấp trình xử lý cho mỗi yêu cầu.
mvmn

4
Với trình xử lý lỗi này, tôi luôn nhận được a ResourceAccessExceptionRestTemplate.doExecutebắt IOExceptions và ném a ResourceAccessException. Tôi đang làm gì sai?
Federico Bellucci,

Tôi đã có thể giải quyết vấn đề này bằng cách mở rộng DefaultResponseErrorHandler.
Crenguta S

48

Bạn nên bắt một HttpStatusCodeExceptionngoại lệ:

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

37
IMO phản hồi phải luôn đi kèm với mã trạng thái thích hợp, nếu không, mục đích của mã là gì.
vaibhav

5
Tôi không chắc chắn để hiểu phản đối @vaibhav: bắt HttpStatusCodeException không phải vì mã sai, nhưng vì trong nhiều trường hợp, một ngoại lệ luôn được ném ra và vì vậy if (mã == giá trị) của bạn không bao giờ có thể được thực thi.
Stefano Scarpanti

1
Các ngoại lệ rất tốn kém trong Java. Không sao cả đối với những nguyên nhân không thường xuyên, không mong muốn (do đó có tên), nhưng thay vào đó, bạn nên tìm giải pháp khác.
Agoston Horvath

11
"Rất tốn kém"? Tốn kém hơn, giả sử, thực hiện một cuộc gọi HTTP?
IcedDante

4
@RaffaelBecharaRameh - HttpStatusCodeException .getResponseBodyAsString () hoặc HttpStatusCodeException.getResponseBodyAsByteArray ().
Dave

45

Spring khéo léo coi mã lỗi http là ngoại lệ và giả định rằng mã xử lý ngoại lệ của bạn có ngữ cảnh để xử lý lỗi. Để trao đổi hoạt động như bạn mong đợi, hãy làm như sau:

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

Điều này sẽ trả về tất cả các kết quả mong đợi từ phản hồi.


2
bạn cần sử dụng HttpClient khác với SDK mặc định, để nhận nội dung phản hồi cho các lỗi
razor

26

Một giải pháp khác là giải pháp được mô tả ở đây ở cuối bài đăng này bởi "enan": 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
bạn cần sử dụng HttpClient khác với SDK mặc định, để nhận nội dung phản hồi cho các lỗi (ví dụ: dấu phẩy apache HttpClient)
razor

17

Spring tóm tắt bạn từ danh sách rất lớn mã trạng thái http. Đó là ý tưởng của các trường hợp ngoại lệ. Hãy xem xét cấu trúc phân cấp org.springframework.web.client.RestClientException:

Bạn có một loạt các lớp để lập bản đồ các tình huống phổ biến nhất khi xử lý các phản hồi http. Danh sách mã http thực sự rất lớn, bạn sẽ không muốn viết mã để xử lý từng tình huống. Nhưng ví dụ, hãy xem xét phân cấp phụ HttpClientErrorException. Bạn có một ngoại lệ duy nhất để ánh xạ bất kỳ loại lỗi 4xx nào. Nếu bạn cần phải đi sâu, sau đó bạn có thể. Nhưng chỉ cần bắt HttpClientErrorException, bạn có thể xử lý mọi tình huống khi dữ liệu xấu được cung cấp cho dịch vụ.

DefaultResponseErrorHandler thực sự đơn giản và chắc chắn. Nếu mã trạng thái phản hồi không phải từ họ 2xx, nó chỉ trả về true cho phương thức hasError.


Dude, cảm ơn vì lời giải thích. Làm thế nào bạn xây dựng cây này với phân cấp ngoại lệ?
đứng một mình

1
Này anh bạn, tôi đã sử dụng Eclipse. Chỉ cần nhấn Control + shift + T để mở trình tìm kiếm loại và nhập RestClientException. Nhấp đúp vào RestClientException từ kết quả, Eclipse sẽ mở lớp đó cho bạn. Sau đó, đưa con trỏ chuột lên tên lớp (nơi có nội dung "public class RestClientException ..." và nhấn Control + T. Bạn sẽ thấy hệ thống phân cấp đó.
Perimosh

bạn đã thử à?
Perimosh

1
Btw trong Intellij nó là: nhấp vào lớp trong cây dự án và Ctrl + Alt + U, hoặc nhấp chuột phải -> Xây dựng sơ đồ
độc lập

3

Nếu bạn sử dụng cơ chế gộp (nhà máy khách http) hoặc cơ chế cân bằng tải (eureka) với của bạn RestTemplate, bạn sẽ không có sự sang trọng của việc tạo new RestTemplatemỗi lớp. Nếu bạn đang gọi nhiều hơn một dịch vụ, bạn không thể sử dụng setErrorHandlervì if sẽ được sử dụng trên toàn cầu cho tất cả các yêu cầu của bạn.

Trong trường hợp này, bắt được HttpStatusCodeExceptioncó vẻ là lựa chọn tốt hơn.

Tùy chọn khác duy nhất mà bạn có là xác định nhiều RestTemplatephiên bản bằng cách sử dụng @Qualifierchú thích.

Ngoài ra - nhưng đây là sở thích của riêng tôi - Tôi thích việc xử lý lỗi của tôi được gắn chặt vào các cuộc gọi của tôi.


3

Tôi đã xử lý điều này như sau:

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

Mã trao đổi dưới đây :

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

Ngoại lệ RestClientExceptionHttpClientErrorExceptionHttpStatusCodeExceptionngoại lệ.

Vì vậy, trong RestTempleteđó có thể xảy ra HttpClientErrorExceptionHttpStatusCodeExceptionngoại lệ. Trong đối tượng ngoại lệ, bạn có thể nhận được thông báo lỗi chính xác bằng cách này:exception.getResponseBodyAsString()

Đây là mã ví dụ :

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

Đây là mô tả mã :

Trong phương thức này, bạn phải vượt qua lớp yêu cầu và phản hồi. Phương thức này sẽ tự động phân tích cú pháp phản hồi dưới dạng đối tượng được yêu cầu.

Trước hết, bạn phải thêm trình chuyển đổi tin nhắn.

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

Sau đó, bạn phải thêm requestHeader. Đây là mã:

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

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

Cuối cùng, bạn phải gọi phương thức trao đổi:

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

Để in sơ bộ, tôi đã sử dụng thư viện Gson. đây là gradle:compile 'com.google.code.gson:gson:2.4'

Bạn chỉ có thể gọi mã dưới đây để nhận phản hồi:

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

Đây là mã làm việc đầy đủ :

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

Cảm ơn :)


2
'org.springframework.web.client.HttpClientErrorException' là một lớp con của 'org.springframework.web.client.HttpStatusCodeException'. Bạn không cần phải bắt HttpClientErrorException nếu bạn đã bắt HttpStatusCodeException và không làm gì khác trong việc xử lý hai ngoại lệ trên.
The-Proton-Resurgence,

0

Một giải pháp rất đơn giản có thể là:

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

Đây là phương pháp POST của tôi với HTTPS trả về nội dung phản hồi cho bất kỳ loại phản hồi xấu nào.

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
bạn cần sử dụng HttpClient khác với SDK mặc định, để nhận nội dung phản hồi cho các lỗi (ví dụ: dấu phẩy apache HttpClient)
razor
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.