JavaWeb application

Obsługa wyjątków w REST API – jak zrobić to elegancko w Spring Boot?

Obsługa wyjątków w REST API

REST API powinno uwzględniać obsługę wyjątków w przypadku kiedy dany zasób nie istnieje, lub pojawi się inny mniej oczekiwany wyjątek. W tym materiale pokazuje jak zaimplementować obsługę wyjątków przy wykorzystaniu AOP – co pozwoli jednocześnie odseparować logikę obsługi błędu.

Odseparowanie logiki wyjątku

Dobrym krokiem pozwalającym na oddzielenie logiki wykonywanej przez REST API od obsługi błędu jest wykorzystanie AOP. AOP, czyli programowanie zorientowane aspektowo pozwala przenieść działanie np. wyjątku do odrębnych klas. W ten sposób nie zaśmiecamy metod webowych i otrzymujemy czysty kod. W związku z tym przypomnienie jak działa AOP znajdziesz w artykule na temat programowania zorientowanego aspektowo.

Kiedy pojawia się problem?

Rozpatrzmy przykład księgarni – użytkownik chce pobrać książkę na podstawie id. Naiwna implementacja wygląda następująco:

@GetMapping("/book/{id}")
public Book getBook(@PathVariable long id) {
    return bookRepo.findById(id).get();
}

Problem pojawia się w momencie, kiedy użytkownik odwołuje się do elementu na liście, którego nie ma. Wówczas aplikacja zwróci mało precyzyjny błąd + najprawdopodobniej błędny kod odpowiedzi -> 500 zamiast 404. Dlatego jako programiści powinniśmy zadbać o prawidłowe zwracanie statusów odpowiedzi.

błędny status odpowidzi
Odpowiedź 500 zamiast 404 w przypadku braku obsługi wyjątku.

Obsługa wyjątków w REST API – implementacja

Po pierwsze dobrze jest wykorzystać metodę orElseThrow na obiekcie pobieranym z repozytorium. Pozwala ona na wywołanie wyjątku w przypadku kiedy dany element nie zostanie odnaleziony.

@GetMapping("/book/{id}")
public Book getBook(@PathVariable long id) {
    return bookRepo.findById(id).orElseThrow(() -> new BookNotFoundException(id));
}

W ramach konstruktora BookNotFoundException możemy przekazać dowolna wartość, jednak dobrą praktyką jest przekazanie bezpośrednio informacji na temat elementu, który stał się przyczyną wywołania wyjątku – po to, aby można było ująć go w komunikacie.

Dlatego w ten sposób, zaimplementowałem nową klasę wyjątku z jasną informacją dla klienta:

public class BookNotFoundException extends RuntimeException {

    public BookNotFoundException(long id) {
        super("Could not find book: " + id);
    }
}

Przechwytywanie obsługi wyjątku

Ostatnim elementem jest przechwycenie obsługi wyjątku przez AOP. Z tego powodu tworzymy handler, który uruchomi się w momencie rzucenia wyjątku i to on będzie dawał odpowiedź klientowi.
Dlatego też posiada adnotacje:

  • @ResponseBody – pozwalająca na przemapowanie formatu na JSON/XML;
  • @ExceptionHandler(BookNotFoundException.class) – która pozwala nasłuchiwanie, kiedy pojawi się podany wyjątek i wówczas podjąć inicjatywę;
  • @ResponseStatus(HttpStatus.NOT_FOUND) – tak, aby status kod odpowiedzi był właściwy;

Cała klasa obsługi błędu wygląda następująco:

@ControllerAdvice
public class BookNotFoundAdvice {

    @ResponseBody
    @ExceptionHandler(BookNotFoundException.class)
    @ResponseStatus(HttpStatus.NOT_FOUND)
    public String bookNotFoundHandler(BookNotFoundException ex) {
        return ex.getMessage();
    }
}

Adnotacja @RestControllerAdvice

Klasa, która posiada tylko porady, które zwracają informacje do klienta może przyjąć uproszczenie adnotacyjne. Zamiast nad każdą metodom pisać osobno @ResponseBody, to można wykorzystać nad klasą adnotacje @RestControllerAdvice, która już w sobie zawiera wyżej wspominaną adnotacje. Wówczas możemy wykorzystać zapis uproszczony:

@RestControllerAdvice
public class BookNotFoundAdvice {

    @ExceptionHandler(BookNotFoundException.class)
    @ResponseStatus(HttpStatus.NOT_FOUND)
    public String bookNotFoundHandler(BookNotFoundException ex) {
        return ex.getMessage();
    }
}

Inne rodzaje błędów i typ zwracany ResponseEntity

W innym artykule opisałem wykorzystanie ResponseEntityExceptionHandler, który w połączeniu z ResponeEntity prezentuje jak można rozbudować omawiany przypadek. Dlatego zachęcam Cię również do jego przeczytania przy okazji uzupełniania wiedzy z tego zakresu.

Obsługa wyjątków w REST API – materiał wideo

Jeśli preferujesz formę wideo, gdzie pokazuje i omawiam w jaki sposób dokonać obsługi wyjątków w REST API, to zapraszam Cię do mojego materiału wideo:

Obsługa wyjątków w REST API – kod źródłowy

Kod do pobrania znajduje się na GitHubie. Enjoy 🙂

Tags:
Show Buttons
Hide Buttons