JavaRush /Blog Java /Random-PL /Obsługa wyjątków w kontrolerach Spring Boot
Павел
Poziom 11

Obsługa wyjątków w kontrolerach Spring Boot

Opublikowano w grupie Random-PL
ZAWARTOŚĆ CYKLU ARTYKUŁÓW Witam ponownie! Czas odkurzyć klawiaturę. Utwórz projekt rozruchu wiosennego. Z zależności mavena potrzebujemy:
<properties>
    <maven.compiler.source>1.8</maven.compiler.source>
    <maven.compiler.target>1.8</maven.compiler.target>
    <java.version>1.8</java.version>
</properties>

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.2.0.RELEASE</version>
    <relativePath/><!-- lookup parent from repository -->
</parent>
<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
</dependencies>
Zanim zaczniesz czytać dalej, utwórz strukturę projektu: Obsługa wyjątków w kontrolerach Spring Boot — 1 BusinessException i CustomException:
public class BusinessException extends Exception{
    public BusinessException(String message) {
        super(message);
    }
}

public class CustomException extends Exception{
    public CustomException(String message) {
        super(message);
    }
}
i klasa odpowiedzi
public class Response {

    private String message;

    public Response() {
    }

    public Response(String message) {
        this.message = message;
    }

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }
}
A teraz zrobię sztuczkę z uszami i oddam głos Aleksiejowi Kutepowowi, który w swoim artykule Obsługa wyjątków w kontrolerach Spring powie nam, jak wypełnić te pliki odpowiednią treścią. Czytaj powoli, starannie przekopiuj wszystkie przykłady do swojego projektu, uruchom i przetestuj w Postmanie. Jeśli w artykule Alexeya następujący wiersz wywołał dla Ciebie pytania: produkuje = APPLICATION_JSON_VALUE , to wiedz, że nie ma to nic wspólnego z obsługą wyjątków, mówi, że domyślnie wszystkie metody tego kontrolera zwracają JSON. Jeśli zajdzie taka potrzeba, w konkretnej metodzie wartość tę można zastąpić innym MediaType.Jeśli przeczytałeś, przejdź dalej. W powyższym artykule omówiono różne opcje dla programów obsługi. Najbardziej elastyczny z nich: @ControllerAdvice - pozwala na zmianę zarówno kodu, jak i treści standardowej odpowiedzi w przypadku błędu. Ponadto umożliwia obsługę wielu wyjątków jednocześnie w jednej metodzie. Ale to nie wszystko, jeśli czytasz dalej, ulepszone @ControllerAdvice otrzymasz całkowicie bezpłatnie. Wykonajmy pewne prace przygotowawcze: chcę, aby odpowiedź wyświetlała zarówno niestandardowe, jak i standardowe komunikaty o błędach. W tym celu dokonajmy zmiany w klasie Response : dodajmy jeszcze jedno pole
private String debugMessage;
Stwórzmy dodatkowy konstruktor:
public Response(String message, String debugMessage) {
    this.message = message;
    this.debugMessage = debugMessage;
}
i nie zapomnij utworzyć modułu pobierającego i ustawiającego dla nowego pola. A teraz do rzeczy. Napiszmy kolejny kontroler:
@RestController
public class Example7Controller {
    @GetMapping(value = "/testExtendsControllerAdvice")
    public ResponseEntity<?> testExtendsControllerAdvice(@RequestBody Response response) {
        return  ResponseEntity.ok(response);
    }
}
Przetestujmy listonosza: wyślij JSON na adres http://localhost:8080/testExtendsControllerAdvice
{
    "message": "message"
}
W odpowiedzi otrzymamy status 200 i ciało
{
    "message": "message",
    "debugMessage": null
}
Teraz wyślemy oczywiście niepoprawny JSON
{
    11"message": "message"
}
W odpowiedzi otrzymamy status 400 (jeśli zapomniałeś co to oznacza, sprawdź w Internecie) i pustą treść odpowiedzi. Oczywiście nikt nie jest z tego zadowolony, walczmy z tym. Wcześniej tworzyliśmy od podstaw @ControllerAdvice , ale w Spring Boot istnieje szablon - ResponseEntityExceptionHandler . Obsługuje już wiele wyjątków, na przykład: NoHandlerFoundException , HttpMessageNotReadableException , MethodArgumentNotValidException i inne. Ta klasa obsługuje błędy. Posiada kilka metod, których nazwy opierają się na zasadzie uchwyt + nazwa wyjątku. Jeśli chcemy obsłużyć jakiś podstawowy wyjątek, to dziedziczymy z tej klasy i nadpisujemy żądaną metodę . Sfinalizujmy domyślną klasę doradczą
@ControllerAdvice
public class DefaultAdvice extends ResponseEntityExceptionHandler {//унаследовались от обработчика-заготовки

    @ExceptionHandler(BusinessException.class)
    public ResponseEntity<Response> handleException(BusinessException e) {
        Response response = new Response(e.getMessage());
        return new ResponseEntity<>(response, HttpStatus.OK);
    }
//Небольшое отступление: В обработчике выше, обратите внимание на HttpStatus.OK,
//он может быть и HttpStatus.BAD_REQUEST Lub другим, тут ограничений нет,
//попробуйте поменять статусы и потестить этот обработчик


    @Override//переопределLub метод родительского класса
    protected ResponseEntity<Object> handleHttpMessageNotReadable
            (HttpMessageNotReadableException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
        Response response = new Response("Не правильный JSON",ex.getMessage());
        return new ResponseEntity<>(response, status);
    }
}
Jak zauważyłeś, procedura obsługi odpowiedzialna za wyjątek HttpMessageNotReadableException została nadpisana . Ten wyjątek występuje, gdy treść żądania przychodzącego do metody kontrolera jest nieczytelna - na przykład niepoprawny JSON. Za ten wyjątek odpowiedzialna jest metoda handleHttpMessageNotReadable () . Złóżmy żądanie ponownie z niepoprawnym JSON: do http://localhost:8080/testExtendsControllerAdvice
{
    11"message": "message"
}
Otrzymujemy odpowiedź z kodem 400 (Bad Request) i treścią:
{
    "message": "Не правильный JSON",
    "debugMessage": "JSON parse error: Unexpected character ('1' (code 49)): was expecting double-quote to start field name; nested exception is com.fasterxml.jackson.core.JsonParseException: Unexpected character ('1' (code 49)): was expecting double-quote to start field name\n at [Source: (PushbackInputStream); line: 2, column: 6]"
}
Teraz odpowiedź zawiera nie tylko poprawny kod, ale także treść z komunikatami informacyjnymi. Sprawdźmy jak to działa z poprawnym żądaniem JSON:
{
    "message": "message"
}
Otrzymaliśmy odpowiedź:
{
    "message": "message",
    "debugMessage": null
}
Szczerze mówiąc, nie podoba mi się, że odpowiedź zawiera pole o wartości null , szybko to naprawimy. Przejdź do klasy Response i umieść adnotację nad wymaganym polem
@JsonInclude(JsonInclude.Include.NON_NULL)
private String debugMessage;
Restartujemy projekt, ponownie wykonujemy poprzednie żądanie i w odpowiedzi otrzymujemy:
{
    "message": "message"
}
Dzięki adnotacji @JsonInclude(JsonInclude.Include.NON_NULL) pole to zostanie uwzględnione w odpowiedzi tylko wtedy, gdy je określimy. @JsonInclude jest zawarty w bibliotece adnotacji Jacksona , bardzo przydatna jest wiedza, co może zrobić. Oto dwa artykuły do ​​wyboru: Adnotacje Jacksona. Autor przetłumaczył, ale nie dokończył tłumaczenia ; Google Translit robi świetną robotę. Walidacja Należy uzupełnić ten temat o takie pojęcie jak walidacja. Mówiąc najprościej, jest to sprawdzenie, czy obiekt jest tym, czego oczekujemy. Przykładowo: jeśli w aplikacji „Książka telefoniczna” musimy sprawdzić obecność numerów telefonów w bazie, to przed wejściem do bazy logiczne jest sprawdzenie, czy użytkownik wpisał litery zamiast cyfr. Trzy artykuły na temat walidacji o rosnącej złożoności: Walidacja komponentów bean na wiosnę Konfigurowanie walidacji DTO w Spring Framework Walidacja danych w Spring Boot Na dzisiaj skończyliśmy z teorią. Na szkolenie proponuję następujące zadanie: Należy zaimplementować aplikację NightclubBouncer. Wymagania: 1) Aplikacja musi akceptować JSON jako dane wejściowe i zapisywać do bazy danych. Przykład JSON-a:
{
    "name": "Katy Perry"
    “status”:super star”
}
Natomiast treść odpowiedzi powinna zawierać następujący napis: Witamy + imię ! 2) Aplikacja musi implementować następujące metody: - wysyłanie rekordu o identyfikatorze z bazy danych do klienta (Postman). - usunięcie rekordu według pola: nazwa . 3) Należy wdrożyć mapowanie z warstwy dto do encji i z powrotem . 4) Aplikacja musi zgłosić błąd KickInTheAssException (musisz go sam opracować), jeśli pole status w przychodzącym JSON nie jest równe: super star 5) Błąd KickInTheAssException musi zostać obsłużony przez ControllerAdvice , a treść odpowiedzi musi zawierać wiadomość: „Niech cię tu więcej nie zobaczę! Status odpowiedzi powinien wynosić 400. 6) Błąd standardowy EntityNotFoundException , który pojawia się jeśli tylko Katy Perry przyszła do klubu i zapisała się do bazy z id = 1 , a Ty wywołałeś metodę „display record by id” i chciałeś aby wyświetlić rekord o id = 2 , którego nie ma w bazie danych. Ten błąd musi zostać obsłużony przez przesłoniętą metodę klasy ResponseEntityExceptionHandler , o której musisz wiedzieć samodzielnie. Odpowiedź musi mieć odpowiedni status. 7) Wykonaj walidację: opcja prosta - pola JSON nie mogą być zerowe, lub trudniejsze, pole "nazwa" musi składać się z dwóch słów alfabetu łacińskiego i oba muszą zaczynać się z dużej litery. Nieprawidłowe wartości powinny zgłosić wyjątek, obsłużyć go w dowolny sposób, wydrukować odpowiedni kod błędu i komunikat o błędzie: No valid. I zaimplementuj to wszystko bez korzystania z biblioteki Lombok, nie dołączaj tego jako zależności projektu 😅
Komentarze
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION