JavaRush /Java Blog /Random-IT /Gestione delle eccezioni nei controller Spring Boot
Павел
Livello 11

Gestione delle eccezioni nei controller Spring Boot

Pubblicato nel gruppo Random-IT
CONTENUTO DEL CICLO DI ARTICOLI Ciao di nuovo! È ora di rispolverare la tastiera. Crea un progetto di avvio primaverile. Dalle dipendenze Maven di cui abbiamo bisogno:
<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>
Prima di leggere oltre, crea una struttura di progetto: Gestione delle eccezioni nei controller Spring Boot - 1 BusinessException e CustomException:
public class BusinessException extends Exception{
    public BusinessException(String message) {
        super(message);
    }
}

public class CustomException extends Exception{
    public CustomException(String message) {
        super(message);
    }
}
e la classe Response
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;
    }
}
E ora farò un giochetto con le mie orecchie e darò la parola ad Alexey Kutepov, nel suo articolo Exception Handling in Spring Controllers, ci dirà come riempire questi file con il contenuto corretto. Leggi lentamente, copia attentamente tutti gli esempi nel tuo progetto, esegui e testa in Postman. Se nell'articolo di Alexey la riga seguente ti ha sollevato delle domande: produce = APPLICATION_JSON_VALUE , allora sappi che non ha nulla a che fare con la gestione delle eccezioni, dice che per impostazione predefinita tutti i metodi di questo controller restituiranno JSON. Se necessario, in un metodo specifico, questo valore può essere sovrascritto con un altro MediaType. Se lo hai letto, vai avanti. L'articolo sopra discute diverse opzioni per i gestori. Il più flessibile: @ControllerAdvice : consente di modificare sia il codice che il corpo della risposta standard in caso di errore. Inoltre, ti consente di gestire più eccezioni contemporaneamente in un unico metodo. Ma non è tutto, se leggi oltre, riceverai il @ControllerAdvice migliorato in modo completamente gratuito. Facciamo un po' di lavoro preparatorio: voglio che la risposta visualizzi sia messaggi di errore personalizzati che standard. Per fare ciò, apportiamo una modifica alla classe Response : aggiungiamo un altro campo
private String debugMessage;
Creiamo un costruttore aggiuntivo:
public Response(String message, String debugMessage) {
    this.message = message;
    this.debugMessage = debugMessage;
}
e non dimenticare di creare un Getter e un Setter per il nuovo campo. Ora arriviamo al punto. Scriviamo un altro controller:
@RestController
public class Example7Controller {
    @GetMapping(value = "/testExtendsControllerAdvice")
    public ResponseEntity<?> testExtendsControllerAdvice(@RequestBody Response response) {
        return  ResponseEntity.ok(response);
    }
}
Proviamo con Postman: invia JSON a http://localhost:8080/testExtendsControllerAdvice
{
    "message": "message"
}
In risposta riceveremo uno status di 200 e un corpo
{
    "message": "message",
    "debugMessage": null
}
Ora invieremo un JSON ovviamente errato
{
    11"message": "message"
}
In risposta, riceveremo lo stato 400 (se hai dimenticato cosa significa, cercalo su Internet) e un corpo della risposta vuoto. Naturalmente nessuno è contento di questo, combattiamolo. In precedenza, abbiamo creato @ControllerAdvice da zero, ma in Spring Boot è disponibile un modello: ResponseEntityExceptionHandler . Gestisce già molte eccezioni, ad esempio: NoHandlerFoundException , HttpMessageNotReadableException , MethodArgumentNotValidException e altri. Questa classe gestisce gli errori. Ha una serie di metodi, i cui nomi si basano sul principio handle + nome dell'eccezione. Se vogliamo gestire qualche eccezione di base, ereditiamo da questa classe e sovrascriviamo il metodo desiderato . Finalizziamo la classe consultiva predefinita
@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 or другим, тут ограничений нет,
//попробуйте поменять статусы и потестить этот обработчик


    @Override//переопределor метод родительского класса
    protected ResponseEntity<Object> handleHttpMessageNotReadable
            (HttpMessageNotReadableException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
        Response response = new Response("Не правильный JSON",ex.getMessage());
        return new ResponseEntity<>(response, status);
    }
}
Come hai notato, il gestore responsabile di HttpMessageNotReadableException è stato sovrascritto . Questa eccezione si verifica quando il corpo della richiesta in arrivo al metodo del controller è illeggibile, ad esempio JSON errato. Il metodo handleHttpMessageNotReadable () è responsabile di questa eccezione . Facciamo di nuovo una richiesta con JSON errato: a http://localhost:8080/testExtendsControllerAdvice
{
    11"message": "message"
}
Riceviamo una risposta con il codice 400 (Bad Request) e il corpo:
{
    "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]"
}
Ora la risposta contiene non solo il codice corretto, ma anche un corpo con messaggi informativi. Controlliamo come funziona con la richiesta JSON corretta:
{
    "message": "message"
}
Abbiamo ricevuto la risposta:
{
    "message": "message",
    "debugMessage": null
}
Ad essere onesti, non mi piace che la risposta contenga un campo con il valore null , lo sistemeremo rapidamente ora. Vai alla classe Response e inserisci un'annotazione nel campo richiesto
@JsonInclude(JsonInclude.Include.NON_NULL)
private String debugMessage;
Riavviamo il progetto, rifacciamo la richiesta precedente e nella risposta otteniamo:
{
    "message": "message"
}
Grazie all'annotazione @JsonInclude(JsonInclude.Include.NON_NULL) questo campo verrà incluso nella risposta solo se lo specifichiamo. @JsonInclude è incluso nella libreria di annotazioni Jackson , è molto utile sapere cosa può fare. Ecco due articoli tra cui scegliere: Annotazioni di Jackson. L'autore ha tradotto, ma non ha completato la traduzione ; Google Translit fa un ottimo lavoro. Convalida È necessario integrare questo argomento con un concetto come la convalida. In poche parole, questo è un controllo che l'oggetto sia l'oggetto che ci aspettiamo. Ad esempio: se nell'applicazione “Rubrica telefonica” dobbiamo verificare la presenza di numeri di telefono nel database, allora prima di entrare nel database è logico verificare se l'utente ha inserito lettere anziché numeri. Tre articoli sulla convalida, in crescente complessità: Convalida dei bean in Spring Impostazione della convalida DTO nel framework Spring Convalida dei dati in Spring Boot Per oggi abbiamo finito con la teoria. Per la formazione, suggerisco il seguente compito: È necessario implementare l'applicazione NightclubBouncer. Requisiti: 1) L'applicazione deve accettare JSON come input e scrivere nel database. Esempio JSON:
{
    "name": "Katy Perry"
    “status”:super star”
}
E il corpo della risposta dovrebbe contenere la seguente scritta: Benvenuto + nome ! 2) L'applicazione deve implementare i seguenti metodi: - emettere un record per ID dal database al client (Postman). - eliminare un record per campo: nome . 3) È necessario implementare la mappatura dal livello dto all'entità e viceversa . 4) L'applicazione deve lanciare un errore KickInTheAssException (devi svilupparlo tu stesso) se il campo di stato nel JSON in entrata non è uguale a: super star 5) L'errore KickInTheAssException deve essere gestito da ControllerAdvice e il corpo della risposta deve contenere l'errore messaggio: “Non farti vedere più qui! Lo stato della risposta dovrebbe essere 400. 6) Errore standard EntityNotFoundException , che si verifica, ad esempio, se solo Katy Perry è venuta al club e ha salvato nel database con id = 1 , e tu hai chiamato il metodo "display record by id" e volevi per visualizzare il record con id = 2 , che non è nel database. Questo errore deve essere gestito da un metodo sovrascritto della classe ResponseEntityExceptionHandler , quale spetta a te capirlo. La risposta deve avere lo stato appropriato. 7) Effettuare la validazione: un'opzione semplice - i campi JSON non devono essere nulli, o più difficile, il campo "nome" deve essere composto da due parole dell'alfabeto latino ed entrambe devono iniziare con una lettera maiuscola. I valori non validi dovrebbero generare un'eccezione, gestirla in qualsiasi modo, stampare il codice di errore appropriato e il messaggio di errore: Nessuna convalida. E implementa tutto questo senza utilizzare la libreria Lombok, non includerla come dipendenza del progetto 😅
Commenti
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION