Egyéni hibaüzenetek kezelése a REST API számára
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 FOLYAMATOT1. Á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ó