Egyéni hibaüzenetek kezelése a REST API számára

REST felső

Most jelentettem be az újat Tanulj tavaszt tanfolyam, amelynek középpontjában az 5. tavasz és a tavaszi bakancs 2 alapjai állnak:

>> ELLENŐRIZZE A FOLYAMATOT

1. Áttekintés

Ebben az oktatóanyagban megvitatjuk, hogyan lehet globális hibakezelőt implementálni a Spring REST API-hoz.

Az egyes kivételek szemantikáját felhasználva értelmes hibaüzeneteket készítünk az ügyfél számára, azzal a egyértelmű céllal, hogy az ügyfélnek minden információt megadjunk a probléma könnyű diagnosztizálásához.

2. Egyéni hibaüzenet

Kezdjük egy egyszerű struktúra megvalósításával a hibák vezetéken keresztül történő küldéséhez - a ApiError:

public class ApiError {private HttpStatus status; privát karakterlánc üzenet; privát lista hibák; public ApiError (HttpStatus status, String message, List list hibák) {super (); this.status = állapot; this.message = üzenet; ez.hibák = hibák; } public ApiError (HttpStatus állapot, String üzenet, String hiba) {super (); this.status = állapot; this.message = üzenet; hibák = tömbök.asList (hiba); }}

Az itt megadott információknak egyértelműnek kell lenniük:

  • állapot: a HTTP állapotkód
  • üzenet: a kivétellel társított hibaüzenet
  • hiba: A felépített hibaüzenetek listája

Természetesen a tényleges tavaszi kivételkezelési logikához a @ControllerAdvice kommentár:

@ControllerAdvice public class CustomRestExceptionHandler kiterjeszti a ResponseEntityExceptionHandler {...}

3. Kezelje a rossz kérelem kivételeit

3.1. A kivételek kezelése

Most nézzük meg, hogyan tudjuk kezelni a leggyakoribb ügyfélhibákat - alapvetően egy ügyfél forgatókönyve érvénytelen kérést küldött az API-nak:

  • BindException: Ez a kivétel akkor fordul elő, amikor végzetes kötési hibák lépnek fel.
  • MethodArgumentNotValidException: Ezt a kivételt akkor dobja el, ha az argumentummal kiegészítve van @Érvényes sikertelen validálás:

@Orride védett ResponseEntity handleMethodArgumentNotValid (MethodArgumentNotValidException ex, HttpHeaders fejlécek, HttpStatus állapot, WebRequest kérés) {List hibák = új ArrayList (); for (FieldError hiba: ex.getBindingResult (). getFieldErrors ()) {errors.add (error.getField () + ":" + error.getDefaultMessage ()); } for (ObjectError error: ex.getBindingResult (). getGlobalErrors ()) {errors.add (error.getObjectName () + ":" + error.getDefaultMessage ()); } ApiError apiError = új ApiError (HttpStatus.BAD_REQUEST, ex.getLocalizedMessage (), hibák); return handleExceptionInternal (ex, apiError, fejlécek, apiError.getStatus (), kérés); } 

Amint látod, felülírunk egy alapmódszert a ResponseEntityExceptionHandler és saját egyedi megvalósításunk biztosítása.

Ez nem mindig lesz így - néha olyan egyedi kivételt kell kezelnünk, amelynek nincs alapértelmezett megvalósítása az alaposztályban, amint később itt láthatjuk.

Következő:

  • MissingServletRequestPartException: Ezt a kivételt akkor dobja el, amikor a többrészes kérés része nem található

  • MissingServletRequestParameterException: Ezt a kivételt akkor dobja el, amikor a kérelem hiányzik:

@Orride védett ResponseEntity handleMissingServletRequestParameter (MissingServletRequestParameterException ex, HttpHeaders fejlécek, HttpStatus állapot, WebRequest kérés) {String error = ex.getParameterName () + "paraméter hiányzik"; ApiError apiError = új ApiError (HttpStatus.BAD_REQUEST, ex.getLocalizedMessage (), hiba); return new ResponseEntity (apiError, new HttpHeaders (), apiError.getStatus ()); }
  • ConstrainViolationException: Ez a kivétel a korlátozások megsértésének eredményéről számol be:

@ExceptionHandler ({ConstraintViolationException.class}) public ResponseEntity handleConstraintViolation (ConstraintViolationException ex, WebRequest kérés) {List hibák = új ArrayList (); for (ConstraintViolation megsértése: ex.getConstraintViolations ()) {hibák.add (fault.getRootBeanClass (). getName () + "" + szabálysértés.getPropertyPath () + ":" + szabálysértés.getMessage ()); } ApiError apiError = új ApiError (HttpStatus.BAD_REQUEST, ex.getLocalizedMessage (), hibák); return new ResponseEntity (apiError, new HttpHeaders (), apiError.getStatus ()); }
  • TypeMismatchException: Ezt a kivételt akkor dobják meg, ha a bab tulajdonságát rossz típussal próbálják meg beállítani.

  • MethodArgumentTypeMismatchException: Ezt a kivételt akkor dobja el, ha a metódus argumentuma nem a várt típus:

@ExceptionHandler ({MethodArgumentTypeMismatchException.class}) public ResponseEntity handleMethodArgumentTypeMismatch (MethodArgumentTypeMismatchException ex, WebRequest request) {String error = ex.getName () + "type:" + ex.getRequired; ApiError apiError = új ApiError (HttpStatus.BAD_REQUEST, ex.getLocalizedMessage (), hiba); return new ResponseEntity (apiError, new HttpHeaders (), apiError.getStatus ()); }

3.2. Az API elfogyasztása az ügyféltől

Most nézzünk meg egy tesztet, amely a MethodArgumentTypeMismatchException: jól küldjön egy kérést a id mint Húr ahelyett hosszú:

@Test public void whenMethodArgumentMismatch_thenBadRequest () {Response response = givenAuth (). Get (URL_PREFIX + "/ api / foos / ccc"); ApiError hiba = response.as (ApiError.class); assertEquals (HttpStatus.BAD_REQUEST, error.getStatus ()); assertEquals (1, error.getErrors (). size ()); assertTrue (error.getErrors (). get (0) .contains ("típusúnak kell lennie"); }

És végül - ugyanezt a kérést figyelembe véve:

Kérési módszer: GET Kérés elérési útja: // localhost: 8080 / spring-security-rest / api / foos / ccc 

Így fog kinézni az ilyen típusú JSON hibaüzenet:

{"status": "BAD_REQUEST", "message": "Nem sikerült átalakítani a [java.lang.String] típusú értéket a kívánt [java.lang.Long] típusra; a beágyazott kivétel a java.lang.NumberFormatException: A bemeneti karakterlánchoz : \ "ccc \" "," hibák ": [" az azonosítónak java.lang.Long típusúnak kell lennie]]}

4. Fogantyú NoHandlerFoundException

Ezután testreszabhatjuk a szervletünket, hogy dobja ezt a kivételt 404 válasz küldése helyett - az alábbiak szerint:

 api org.springframework.web.servlet.DispatcherServlet throwExceptionIfNoHandlerFound true 

Aztán, ha ez megtörténik, egyszerűen kezelhetjük, mint bármely más kivételt:

@Orride védett ResponseEntity handleNoHandlerFoundException (NoHandlerFoundException ex, HttpHeaders fejlécek, HttpStatus állapot, WebRequest kérés) {String error = "Nem található kezelő" + ex.getHttpMethod () + "" + ex.getRequestURL (); ApiError apiError = új ApiError (HttpStatus.NOT_FOUND, ex.getLocalizedMessage (), hiba); return new ResponseEntity (apiError, new HttpHeaders (), apiError.getStatus ()); }

Itt van egy egyszerű teszt:

@Test public void whenNoHandlerForHttpRequest_thenNotFound () {Response response = givenAuth (). Delete (URL_PREFIX + "/ api / xx"); ApiError hiba = response.as (ApiError.class); assertEquals (HttpStatus.NOT_FOUND, error.getStatus ()); assertEquals (1, error.getErrors (). size ()); assertTrue (error.getErrors (). get (0) .contains ("Nem található kezelő"); }

Vessünk egy pillantást a teljes kérésre:

Kérési módszer: DELETE Kérés elérési útja: // localhost: 8080 / spring-security-rest / api / xx

És a hiba JSON válasz:

{"status": "NOT_FOUND", "message": "Nem található kezelő a DELETE / spring-security-rest / api / xx fájlhoz", "hibák": ["Nem található kezelő a DELETE / spring-security-rest / api kereséshez / xx "]}

5. Fogantyú HttpRequestMethodNotSupportedException

Ezután nézzünk meg egy másik érdekes kivételt - a HttpRequestMethodNotSupportedException - amely akkor fordul elő, amikor nem támogatott HTTP módszerrel küld egy kértet:

@Orride védett ResponseEntity handleHttpRequestMethodNotSupported (HttpRequestMethodNotSupportedException ex, HttpHeaders fejlécek, HttpStatus állapot, WebRequest kérés) {StringBuilder builder = new StringBuilder (); builder.append (ex.getMethod ()); builder.append ("a metódus nem támogatott ehhez a kéréshez. A támogatott módszerek a következők:"); ex.getSupportedHttpMethods (). forEach (t -> builder.append (t + "")); ApiError apiError = új ApiError (HttpStatus.METHOD_NOT_ALLOWED, ex.getLocalizedMessage (), builder.toString ()); return new ResponseEntity (apiError, new HttpHeaders (), apiError.getStatus ()); }

Itt van egy egyszerű teszt, amely ezt a kivételt reprodukálja:

@Test public void whenHttpRequestMethodNotSupported_thenMethodNotAllowed () {Response response = megadottAuth (). Delete (URL_PREFIX + "/ api / foos / 1"); ApiError hiba = response.as (ApiError.class); assertEquals (HttpStatus.METHOD_NOT_ALLOWED, error.getStatus ()); assertEquals (1, error.getErrors (). size ()); assertTrue (error.getErrors (). get (0) .contains ("A támogatott módszerek")); }

És itt van a teljes kérés:

Kérési módszer: DELETE Kérés elérési útja: // localhost: 8080 / spring-security-rest / api / foos / 1

És a hiba JSON válasz:

{"status": "METHOD_NOT_ALLOWED", "message": "A" DELETE "kérési módszer nem támogatott", "hibák": ["A DELETE metódus nem támogatott ehhez a kéréshez. A támogatott módszerek a GET"]}

6. Fogantyú HttpMediaTypeNotSupportedException

Most kezeljük HttpMediaTypeNotSupportedException - amely akkor fordul elő, amikor az ügyfél nem támogatott adathordozó-típusú kérelmet küld - az alábbiak szerint:

@Orride védett ResponseEntity handleHttpMediaTypeNotSupported (HttpMediaTypeNotSupportedException ex, HttpHeaders fejlécek, HttpStatus állapot, WebRequest kérés) {StringBuilder builder = new StringBuilder (); builder.append (ex.getContentType ()); builder.append ("a médiatípus nem támogatott. A támogatott médiatípusok a következők"); ex.getSupportedMediaTypes (). forEach (t -> builder.append (t + ",")); ApiError apiError = új ApiError (HttpStatus.UNSUPPORTED_MEDIA_TYPE, ex.getLocalizedMessage (), builder.substring (0, builder.length () - 2)); return new ResponseEntity (apiError, new HttpHeaders (), apiError.getStatus ()); }

Itt van egy egyszerű teszt, amely a problémát futtatja:

@Test public void whenSendInvalidHttpMediaType_thenUnsupportedMediaType () {Response response = givenAuth (). Body (""). Post (URL_PREFIX + "/ api / foos"); ApiError hiba = response.as (ApiError.class); assertEquals (HttpStatus.UNSUPPORTED_MEDIA_TYPE, error.getStatus ()); assertEquals (1, error.getErrors (). size ()); assertTrue (error.getErrors (). get (0) .contains ("a médiatípus nem támogatott"); }

Végül - íme egy minta kérés:

Kérési módszer: POST Kérés elérési útja: // localhost: 8080 / spring-security- Fejlécek: Content-Type = text / plain; karakterkészlet = ISO-8859-1

És a hiba JSON válasz:

{"status": "UNSUPPORTED_MEDIA_TYPE", "message": "Tartalomtípus 'text / plain; charset = ISO-8859-1' nem támogatott", "hibák": ["text / plain; charset = ISO-8859-1 médiatípus nem támogatott. Támogatott médiatípusok: text / xml application / x-www-form-urlencoded application / * + xml application / json; charset = UTF-8 application / * + json; charset = UTF-8 * / " ]}

7. Alapértelmezett kezelő

Végül valósítsunk meg egy tartalék kezelőt - egy mindenre kiterjedő logikát, amely minden más kivétellel foglalkozik, amelyeknek nincsenek konkrét kezelői:

@ExceptionHandler ({Exception.class}) public ResponseEntity handleAll (Exception ex, WebRequest request) {ApiError apiError = new ApiError (HttpStatus.INTERNAL_SERVER_ERROR, ex.getLocalizedMessage (), "hiba történt"); return new ResponseEntity (apiError, new HttpHeaders (), apiError.getStatus ()); }

8. Következtetés

Megfelelő, kiforrott hibakezelő építése a Spring REST API számára nehéz és mindenképpen iteratív folyamat. Remélhetőleg ez az oktatóanyag jó kiindulópont lesz az Ön API-jának megvalósításához, és jó horgony ahhoz, hogy miként kell segíteni az API ügyfeleinek a hibák gyors és könnyű diagnosztizálásában és azokon való elmozdulásában.

A teljes végrehajtása ennek az oktatóanyagnak a Github projektje található meg - ez egy Eclipse alapú projekt, ezért könnyen importálhatónak és futtathatónak kell lennie.

REST alsó

Most jelentettem be az újat Tanulj tavaszt tanfolyam, amelynek középpontjában az 5. tavasz és a tavaszi bakancs 2 alapjai állnak:

>> ELLENŐRIZZE A FOLYAMATOT