JavaRush /Blog Java /Random-ES /Manejo de excepciones en controladores Spring Boot
Павел
Nivel 11

Manejo de excepciones en controladores Spring Boot

Publicado en el grupo Random-ES
CONTENIDOS DEL CICLO DE ARTÍCULOS ¡Hola de nuevo! Es hora de desempolvar tu teclado. Cree un proyecto de arranque de primavera. De las dependencias de Maven necesitamos:
<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>
Antes de seguir leyendo, cree una estructura de proyecto: Manejo de excepciones en controladores Spring Boot - 1 BusinessException y CustomException:
public class BusinessException extends Exception{
    public BusinessException(String message) {
        super(message);
    }
}

public class CustomException extends Exception{
    public CustomException(String message) {
        super(message);
    }
}
y la clase de respuesta
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;
    }
}
Y ahora, haré un truco con mis oídos y le daré la palabra a Alexey Kutepov, en su artículo Manejo de excepciones en controladores Spring, nos dirá cómo llenar estos archivos con el contenido correcto. Lea despacio, copie cuidadosamente todos los ejemplos en su proyecto, ejecútelos y pruébelos en Postman. Si en el artículo de Alexey la siguiente línea le generó preguntas: produce = APPLICATION_JSON_VALUE , entonces sepa que no tiene nada que ver con el manejo de excepciones, dice que de forma predeterminada todos los métodos de este controlador devolverán JSON. Si es necesario, en un método específico, este valor se puede anular a otro MediaType. Si lo has leído, continúa. El artículo anterior analiza diferentes opciones para los controladores. El más flexible de ellos: @ControllerAdvice : le permite cambiar tanto el código como el cuerpo de la respuesta estándar en caso de error. Además, le permite manejar múltiples excepciones a la vez con un solo método. Pero eso no es todo, si sigues leyendo, obtendrás el @ControllerAdvice mejorado de forma totalmente gratuita. Hagamos un poco de trabajo preparatorio: quiero que la respuesta muestre mensajes de error estándar y personalizados. Para hacer esto, hagamos un cambio en la clase Respuesta : agregue un campo más
private String debugMessage;
Creemos un constructor adicional:
public Response(String message, String debugMessage) {
    this.message = message;
    this.debugMessage = debugMessage;
}
y no olvide crear un Getter y Setter para el nuevo campo. Ahora al grano. Escribamos otro controlador:
@RestController
public class Example7Controller {
    @GetMapping(value = "/testExtendsControllerAdvice")
    public ResponseEntity<?> testExtendsControllerAdvice(@RequestBody Response response) {
        return  ResponseEntity.ok(response);
    }
}
Probemos en cartero: envíe JSON a http://localhost:8080/testExtendsControllerAdvice
{
    "message": "message"
}
En respuesta recibiremos un estado de 200 y un cuerpo.
{
    "message": "message",
    "debugMessage": null
}
Ahora enviaremos JSON obviamente incorrecto.
{
    11"message": "message"
}
En respuesta, recibiremos un estado de 400 (si has olvidado lo que significa, búscalo en Internet) y un cuerpo de respuesta vacío. Por supuesto, nadie está contento con esto, luchemos. Anteriormente, creamos @ControllerAdvice desde cero, pero en Spring Boot hay una plantilla: ResponseEntityExceptionHandler . Ya maneja muchas excepciones, por ejemplo: NoHandlerFoundException , HttpMessageNotReadableException , MethodArgumentNotValidException y otras. Esta clase maneja errores. Tiene un montón de métodos, cuyos nombres se basan en el identificador principal + nombre de excepción. Si queremos manejar alguna excepción básica, heredamos de esta clase y anulamos el método deseado . Finalicemos la clase de asesoramiento predeterminada.
@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 o другим, тут ограничений нет,
//попробуйте поменять статусы и потестить этот обработчик


    @Override//переопределo метод родительского класса
    protected ResponseEntity<Object> handleHttpMessageNotReadable
            (HttpMessageNotReadableException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
        Response response = new Response("Не правильный JSON",ex.getMessage());
        return new ResponseEntity<>(response, status);
    }
}
Como habrás notado, el controlador responsable de HttpMessageNotReadableException ha sido anulado . Esta excepción ocurre cuando el cuerpo de la solicitud que llega al método del controlador es ilegible (por ejemplo, JSON incorrecto). El método handleHttpMessageNotReadable () es responsable de esta excepción . Hagamos una solicitud nuevamente con JSON incorrecto: a http://localhost:8080/testExtendsControllerAdvice
{
    11"message": "message"
}
Recibimos una respuesta con el código 400 (Solicitud incorrecta) y el cuerpo:
{
    "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]"
}
Ahora la respuesta contiene no sólo el código correcto, sino también un cuerpo con mensajes informativos. Veamos cómo funciona con la solicitud JSON correcta:
{
    "message": "message"
}
Recibimos la respuesta:
{
    "message": "message",
    "debugMessage": null
}
Para ser honesto, no me gusta que la respuesta contenga un campo con el valor null , lo solucionaremos rápidamente ahora. Vaya a la clase Respuesta y coloque una anotación sobre el campo requerido
@JsonInclude(JsonInclude.Include.NON_NULL)
private String debugMessage;
Reiniciamos el proyecto, volvemos a realizar la solicitud anterior, y en la respuesta obtenemos:
{
    "message": "message"
}
Gracias a la anotación @JsonInclude(JsonInclude.Include.NON_NULL) , este campo solo se incluirá en la respuesta si lo especificamos. @JsonInclude está incluido en la biblioteca de anotaciones de Jackson , es muy útil saber qué puede hacer. Aquí hay dos artículos para elegir: Anotaciones de Jackson. El autor tradujo, pero no completó la traducción ; Google Translit hace un gran trabajo. Validación Es necesario complementar este tema con un concepto como validación. En pocas palabras, se trata de comprobar que el objeto es el objeto que esperamos. Por ejemplo: si en la aplicación "Directorio telefónico" necesitamos verificar la presencia de números de teléfono en la base de datos, antes de ingresar a la base de datos, es lógico verificar si el usuario ha ingresado letras en lugar de números. Tres artículos sobre validación, en complejidad creciente: Validación de beans en Spring Configuración de la validación DTO en Spring Framework Validación de datos en Spring Boot Hemos terminado con la teoría por hoy. Para la formación, sugiero la siguiente tarea: Necesita implementar la aplicación NightclubBouncer. Requisitos: 1) La aplicación debe aceptar JSON como entrada y escribir en la base de datos. Ejemplo JSON:
{
    "name": "Katy Perry"
    “status”:super star”
}
Y el cuerpo de la respuesta debe contener la siguiente inscripción: ¡Bienvenido + nombre ! 2) La aplicación debe implementar los siguientes métodos: - enviar un registro por identificación desde la base de datos al cliente (Postman). - eliminar un registro por campo: nombre . 3) Se debe implementar el mapeo desde la capa dto a la entidad y viceversa . 4) La aplicación debe generar un error KickInTheAssException (debe desarrollarlo usted mismo) si el campo de estado en el JSON entrante no es igual a: super star 5) El error KickInTheAssException debe ser manejado por ControllerAdvice y el cuerpo de la respuesta debe contener el mensaje: “¡No dejes que te vuelva a ver aquí! El estado de la respuesta debe ser 400. 6) Error estándar EntityNotFoundException , que ocurre, por ejemplo, si solo Katy Perry vino al club y guardó en la base de datos con id = 1 , y usted llamó al método "mostrar registro por id" y quiso para mostrar el registro con id = 2 , que no está en la base de datos. Este error debe ser manejado por un método anulado de la clase ResponseEntityExceptionHandler , cuál depende de usted descubrirlo usted mismo. La respuesta debe tener el estado apropiado. 7) Haga la validación: una opción simple: los campos JSON no deben ser nulos, o más difícil, el campo "nombre" debe constar de dos palabras del alfabeto latino y ambas deben comenzar con una letra mayúscula. Los valores no válidos deben generar una excepción, manejarlo de cualquier manera, imprimir el código de error apropiado y el mensaje de error: No validar. E implemente todo esto sin usar la biblioteca Lombok, no la incluya como dependencia del proyecto 😅
Comentarios
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION