การจัดการข้อยกเว้น 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

คำตอบ:


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
ฉันมี RestTemplate 1 อินสแตนซ์ที่ใช้ซ้ำสำหรับการเรียกอื่น ฉันต้องการจัดการข้อผิดพลาดจากการโทรที่แตกต่างกัน - เห็นได้ชัดว่าไม่มีวิธีใดที่จะทำเช่นนั้นกับตัวจัดการส่วนกลาง - ฉันจำเป็นต้องให้ตัวจัดการต่อคำขอ
mvmn

4
ด้วยตัวจัดการข้อผิดพลาดนี้ฉันมักจะได้รับResourceAccessExceptionเนื่องจากRestTemplate.doExecuteจับIOExceptions และพ่นไฟล์ResourceAccessException. ผมทำอะไรผิดหรือเปล่า?
Federico Bellucci

ฉันสามารถแก้ปัญหานี้ได้โดยการขยาย DefaultResponseErrorHandler
Crenguta S

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 หรือไม่
IcedDante

4
@RaffaelBecharaRameh - HttpStatusCodeException .getResponseBodyAsString () หรือ HttpStatusCodeException.getResponseBodyAsByteArray ()
Dave

45

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

สิ่งนี้จะส่งคืนผลลัพธ์ที่คาดหวังทั้งหมดจากการตอบกลับ


2
คุณต้องใช้ HttpClient ที่แตกต่างจาก SDK เริ่มต้นเพื่อรับเนื้อหาตอบสนองสำหรับข้อผิดพลาด
มีดโกน

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
คุณต้องใช้ HttpClient ที่แตกต่างจาก SDK เริ่มต้นเพื่อรับเนื้อหาตอบสนองสำหรับข้อผิดพลาด (ตัวอย่างเช่น apache commons HttpClient)
มีดโกน

17

Spring สรุปคุณจากรายการรหัสสถานะ http ที่มีขนาดใหญ่มาก นั่นคือแนวคิดของข้อยกเว้น ดูลำดับชั้น org.springframework.web.client.RestClientException:

คุณมีคลาสมากมายในการแมปสถานการณ์ที่พบบ่อยที่สุดเมื่อจัดการกับการตอบสนอง http รายการรหัส http มีขนาดใหญ่มากคุณไม่ต้องการเขียนโค้ดเพื่อจัดการกับแต่ละสถานการณ์ แต่ตัวอย่างเช่นลองดูที่ลำดับชั้นย่อยของ HttpClientErrorException คุณมีข้อยกเว้นเพียงครั้งเดียวในการแมปข้อผิดพลาดประเภท 4xx หากคุณต้องการลงลึกคุณก็ทำได้ แต่เพียงแค่จับ HttpClientErrorException คุณสามารถจัดการกับสถานการณ์ใด ๆ ที่มีการให้ข้อมูลที่ไม่ดีแก่บริการ

DefaultResponseErrorHandler นั้นเรียบง่ายและมั่นคงจริงๆ หากรหัสสถานะการตอบกลับไม่ได้มาจากตระกูล 2xx ก็จะส่งคืนจริงสำหรับเมธอด hasError


ขอบคุณสำหรับคำอธิบาย คุณสร้างต้นไม้นี้โดยมีลำดับชั้นข้อยกเว้นได้อย่างไร
stand alone

1
สวัสดีฉันใช้ Eclipse เพียงกด control + shift + T เพื่อเปิดตัวค้นหาประเภทและพิมพ์ RestClientException ดับเบิลคลิกที่ RestClientException จากผลลัพธ์ Eclipse จะเปิดคลาสนั้นให้คุณ จากนั้นวางเคอร์เซอร์ไว้เหนือชื่อคลาส (โดยระบุว่า "คลาสสาธารณะ RestClientException ... " แล้วกด control + T คุณจะเห็นลำดับชั้นนั้น
Perimosh

คุณลองดูไหม
Perimosh

1
Btw ใน Intellij คือ: คลิกที่คลาสในแผนผังโครงการและ Ctrl + Alt + U หรือคลิกเมาส์ขวา -> สร้างแผนภาพ
สแตนด์อะโลน

3

หากคุณใช้กลไกพูลลิ่ง (โรงงานไคลเอ็นต์ http) หรือโหลดบาลานซ์ (ยูเรก้า) ร่วมกับของ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มีข้อยกเว้นHttpClientErrorExceptionและHttpStatusCodeExceptionข้อยกเว้น

ดังนั้นในRestTempleteนั้นอาจเกิดขึ้นHttpClientErrorExceptionและมีHttpStatusCodeExceptionข้อยกเว้น ในวัตถุข้อยกเว้นคุณจะได้รับข้อความแสดงข้อผิดพลาดที่แน่นอนโดยใช้วิธีนี้: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' คุณไม่จำเป็นต้องจับ HttpClientErrorException หากคุณจับ HttpStatusCodeException อยู่แล้วและไม่ได้ทำอะไรที่แตกต่างในการจัดการข้อยกเว้นสองข้อข้างต้น
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 ซึ่งส่งคืนเนื้อหาตอบสนองสำหรับการตอบสนองที่ไม่ดีทุกประเภท

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
คุณต้องใช้ HttpClient ที่แตกต่างจาก SDK เริ่มต้นเพื่อรับเนื้อหาตอบสนองสำหรับข้อผิดพลาด (ตัวอย่างเช่น apache commons HttpClient)
มีดโกน
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.