Saltar al contenido

Manejo de excepciones del servicio Spring Boot REST

Si encuentras algo que no comprendes puedes dejarlo en los comentarios y te ayudaremos tan rápido como podamos.

Solución:

Nueva respuesta (2016-04-20)

Usando Spring Boot 1.3.1.RELEASE

Nuevo Paso 1 – Es fácil y menos intrusivo agregar las siguientes propiedades a application.properties:

spring.mvc.throw-exception-if-no-handler-found=true
spring.resources.add-mappings=false

¡Mucho más fácil que modificar la instancia DispatcherServlet existente (como se muestra a continuación)! – JO’

Si trabaja con una aplicación RESTful completa, es muy importante deshabilitar el mapeo automático de static recursos ya que si está utilizando la configuración predeterminada de Spring Boot para manejar static entonces el controlador de recursos manejará la solicitud (se ordena en último lugar y se asigna a /**, lo que significa que recoge cualquier solicitud que no haya sido manejada por ningún otro controlador en la aplicación) para que el servlet despachador no obtenga la oportunidad de lanzar una excepción.


Nueva respuesta (2015-12-04)

Usando Spring Boot 1.2.7.RELEASE

Nuevo Paso 1 – Encontré una forma mucho menos intrusiva de configurar el indicador “throExceptionIfNoHandlerFound”. Reemplace el código de reemplazo de DispatcherServlet a continuación (Paso 1) con esto en la clase de inicialización de su aplicación:

@ComponentScan()
@EnableAutoConfiguration
public class MyApplication extends SpringBootServletInitializer {
    private static Logger LOG = LoggerFactory.getLogger(MyApplication.class);
    public static void main(String[] args) 
        ApplicationContext ctx = SpringApplication.run(MyApplication.class, args);
        DispatcherServlet dispatcherServlet = (DispatcherServlet)ctx.getBean("dispatcherServlet");
        dispatcherServlet.setThrowExceptionIfNoHandlerFound(true);
    

En este caso, estamos configurando el indicador en el DispatcherServlet existente, que conserva cualquier configuración automática del marco Spring Boot.

Una cosa más que encontré: la anotación @EnableWebMvc es mortal para Spring Boot. Sí, esa anotación permite cosas como poder capturar todas las excepciones del controlador como se describe a continuación, pero también elimina MUCHA de la útil configuración automática que normalmente proporcionaría Spring Boot. Use esa anotación con extrema precaución cuando use Spring Boot.


Respuesta original:

Después de mucha más investigación y seguimiento de las soluciones publicadas aquí (¡gracias por la ayuda!) y una cantidad no pequeña de rastreo de tiempo de ejecución en el código Spring, finalmente encontré una configuración que manejará todas las excepciones (no errores, pero sigue leyendo) incluidos los 404.

Paso 1 – dígale a SpringBoot que deje de usar MVC para situaciones de “controlador no encontrado”. Queremos que Spring lance una excepción en lugar de devolverle al cliente una vista redireccionada a “/ error”. Para hacer esto, necesita tener una entrada en una de sus clases de configuración:

// NEW CODE ABOVE REPLACES THIS! (2015-12-04)
@Configuration
public class MyAppConfig 
    @Bean  // Magic entry 
    public DispatcherServlet dispatcherServlet() 
        DispatcherServlet ds = new DispatcherServlet();
        ds.setThrowExceptionIfNoHandlerFound(true);
        return ds;
    

La desventaja de esto es que reemplaza el servlet de despachador predeterminado. Esto no ha sido un problema para nosotros todavía, sin que aparezcan efectos secundarios o problemas de ejecución. Si va a hacer algo más con el servlet de despachador por otras razones, este es el lugar para hacerlo.

Paso 2 – Ahora que Spring Boot arrojará una excepción cuando no se encuentre un controlador, esa excepción se puede manejar con cualquier otra en un controlador de excepciones unificado:

@EnableWebMvc
@ControllerAdvice
public class ServiceExceptionHandler extends ResponseEntityExceptionHandler 

    @ExceptionHandler(Throwable.class)
    @ResponseBody
    ResponseEntity handleControllerException(HttpServletRequest req, Throwable ex) 
        ErrorResponse errorResponse = new ErrorResponse(ex);
        if(ex instanceof ServiceException) 
            errorResponse.setDetails(((ServiceException)ex).getDetails());
        
        if(ex instanceof ServiceHttpException) 
            return new ResponseEntity(errorResponse,((ServiceHttpException)ex).getStatus());
         else 
            return new ResponseEntity(errorResponse,HttpStatus.INTERNAL_SERVER_ERROR);
        
    

    @Override
    protected ResponseEntity handleNoHandlerFoundException(NoHandlerFoundException ex, HttpHeaders headers, HttpStatus status, WebRequest request) 
        Map responseBody = new HashMap<>();
        responseBody.put("path",request.getContextPath());
        responseBody.put("message","The URL you have reached is not in service at this time (404).");
        return new ResponseEntity(responseBody,HttpStatus.NOT_FOUND);
    
    ...


Tenga en cuenta que creo que la anotación “@EnableWebMvc” es importante aquí. Parece que nada de esto funciona sin él. Y eso es todo: su aplicación Spring Boot ahora detectará todas las excepciones, incluidas las 404, en la clase de controlador anterior y puede hacer lo que quiera con ellas.

Un último punto: no parece haber una manera de hacer que esto atrape los errores lanzados. Tengo una idea loca de usar aspectos para detectar errores y convertirlos en Excepciones que el código anterior pueda manejar, pero aún no he tenido tiempo de intentar implementar eso. Espero que esto ayude a alguien.

Cualquier comentario/correcciones/mejoras serán apreciadas.

Con Spring Boot 1.4+, se agregaron nuevas clases geniales para un manejo de excepciones más fácil que ayuda a eliminar el código repetitivo.

un nuevo @RestControllerAdvice se proporciona para el manejo de excepciones, es una combinación de @ControllerAdvice y @ResponseBody. Puedes quitar el @ResponseBody sobre el @ExceptionHandler cuando use esta nueva anotación.

es decir

@RestControllerAdvice
public class GlobalControllerExceptionHandler 

    @ExceptionHandler(value =  Exception.class )
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    public ApiErrorResponse unknownException(Exception ex, WebRequest req) 
        return new ApiErrorResponse(...);
    

Para manejar errores 404 agregando @EnableWebMvc anotación y lo siguiente a application.properties fue suficiente:
spring.mvc.throw-exception-if-no-handler-found=true

Puedes encontrar y jugar con las fuentes aquí:
https://github.com/magiccrafter/spring-boot-exception-handling

creo ResponseEntityExceptionHandler cumple con sus requisitos. Un fragmento de código de muestra para HTTP 400:

@ControllerAdvice
public class MyExceptionHandler extends ResponseEntityExceptionHandler 

  @ResponseStatus(value = HttpStatus.BAD_REQUEST)
  @ExceptionHandler(HttpMessageNotReadableException.class, MethodArgumentNotValidException.class,
      HttpRequestMethodNotSupportedException.class)
  public ResponseEntity badRequest(HttpServletRequest req, Exception exception) 
    // ...
  


Puedes revisar esta publicación

Sección de Reseñas y Valoraciones

Nos encantaría que puedieras difundir esta reseña si te ayudó.

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Respuestas a preguntas comunes sobre programacion y tecnología