restcontrolleradvice mvc manejo formulario form excepciones example ejemplo docs controlleradvice spring spring-mvc interceptor custom-exceptions

manejo - spring mvc formulario ejemplo



Controlador de excepciones en Spring MVC (3)

Quiero crear un controlador de excepciones que intercepte todos los controladores en mi proyecto. ¿Es eso posible hacer? Parece que tengo que poner un método de controlador en cada controlador. Gracias por tu ayuda. Tengo un controlador de resorte que envía la respuesta de Json. Entonces, si ocurre una excepción, quiero enviar una respuesta de error que se puede controlar desde un lugar.


Desde la primavera 3.2 puede usar la anotación @ControllerAdvice . Puede declarar un método @ExceptionHandler dentro de una clase @ControllerAdvice, en cuyo caso maneja las excepciones de los métodos @RequestMapping de todos los controladores.

@ControllerAdvice public class MyGlobalExceptionHandler { @ExceptionHandler(value=IOException.class) public @ResponseBody String iOExceptionHandler(Exception ex){ // // } // other exception handler methods // ... }


Una clase abstracta donde se definen los controladores de excepción. Y luego haz que tus controladores lo hereden.


(Encontré una manera de implementarlo en Spring 3.1, esto se describe en la segunda parte de esta respuesta)

Vea el capítulo 16.11 Manejo de excepciones de Spring Reference.

Hay algunas formas más que usar @ExceptionHandler (ver la respuesta de gouki )

  • Podría implementar un HandlerExceptionResolver ( use el servlet no el paquete de portlet ), es decir, algún tipo de @ExceptionHandler global
  • Si no tiene una lógica específica para la excepción, pero solo una vista específica, entonces puede usar SimpleMappingExceptionResolver , que es al menos una implementación de HandlerExceptionResolver donde puede especificar un patrón de nombre de excepción y la vista (jsp) que se muestra cuando Se lanza la excepción. Por ejemplo:

    <bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver" p:defaultErrorView="uncaughtException"> <property name="exceptionMappings"> <props> <prop key=".DataAccessException">dataAccessFailure</prop> <prop key=".TypeMismatchException">resourceNotFound</prop> <prop key=".AccessDeniedException">accessDenied</prop> </props> </property> </bean>

En Spring 3.2+ se puede anotar una clase con @ControllerAdvice , todos los métodos @ExceptionHandler en esta clase funcionan de manera global.

En la primavera 3.1 no hay @ControllerAdvice . Pero con un pequeño truco se podría tener una característica similar.

La clave es la comprensión de cómo funciona @ExceptionHandler . En la primavera 3.1 hay una clase ExceptionHandlerExceptionResolver . Esta clase implementa (con la ayuda de sus superclases) la interfaz HandlerExceptionResolver y es responsable de invocar los métodos @ExceptionHandler .

La interfaz HandlerExceptionResolver tiene un solo método:

ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex);`.

Cuando la solicitud fue manejada por un Método de Controlador Spring 3.x, entonces este método (representado por org.springframework.web.method.HandlerMethod ) es el parámetro del handler .

El ExceptionHandlerExceptionResolver utiliza el handler ( HandlerMethod ) para obtener la clase de Controlador y escanearlo en busca de métodos anotados con @ExceptionHandler . Si uno de estos métodos coincide con la excepción ( ex ), estos métodos se invocan para manejar la excepción. (de lo contrario, null para indicar que este solucionador de excepciones no se siente responsable).

La primera idea sería implementar un HandlerExceptionResolver propio que se comporte como ExceptionHandlerExceptionResolver , pero en lugar de buscar @ExceptionHandler en la clase de controlador, debería buscarlos en un bean especial. El inconveniente sería que uno tiene que (copiar (o subclase ExceptionHandlerExceptionResolver ) y debe) configurar todos los convertidores de mensajes agradables, solucionadores de argumentos y manejadores de valores de retorno a mano (la configuración del único y ExceptionHandlerExceptionResolver se realiza de manera automática). Entonces se me ocurrió otra idea:

Implemente un HandlerExceptionResolver simple que " HandlerExceptionResolver " la excepción a THE (ya configurado) ExceptionHandlerExceptionResolver , PERO con un handler modificado que apunta al bean que contiene los controladores de excepciones globales. (Los llamo globales, porque funcionan para todos los controladores).

Y esta es la implementación: GlobalMethodHandlerExeptionResolver

import java.util.List; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.Ordered; import org.springframework.util.StringUtils; import org.springframework.web.method.HandlerMethod; import org.springframework.web.servlet.HandlerExceptionResolver; import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver; public class GlobalMethodHandlerExeptionResolver implements HandlerExceptionResolver, Ordered { @Override public int getOrder() { return -1; // } private ExceptionHandlerExceptionResolver realExceptionResolver; private List<GlobalMethodExceptionResolverContainer> containers; @Autowired public GlobalMethodHandlerExeptionResolver( ExceptionHandlerExceptionResolver realExceptionResolver, List<GlobalMethodExceptionResolverContainer> containers) { this.realExceptionResolver = realExceptionResolver; this.containers = containers; } @Override public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) { for (GlobalMethodExceptionResolverContainer container : this.containers) { ModelAndView result = this.realExceptionResolver.resolveException( request, response, handlerMethodPointingGlobalExceptionContainerBean(container), ex); if (result != null) return result; } // we feel not responsible return null; } protected HandlerMethod handlerMethodPointingGlobalExceptionContainerBean( GlobalMethodExceptionResolverContainer container) { try { return new HandlerMethod(container, GlobalMethodExceptionResolverContainer.class. getMethod("fakeHanderMethod")); } catch (NoSuchMethodException | SecurityException e) { throw new RuntimeException(e); } } }

El handler global tiene que implementar esta interfaz (para poder encontrar e implementar el fakeHanderMethod utilizado para el handler

public interface GlobalMethodExceptionResolverContainer { void fakeHanderMethod(); }

Y ejemplo para un controlador global:

@Component public class JsonGlobalExceptionResolver implements GlobalMethodExceptionResolverContainer { @Override public void fakeHanderMethod() { } @ExceptionHandler(MethodArgumentNotValidException.class) @ResponseStatus(HttpStatus.BAD_REQUEST) @ResponseBody public ValidationErrorDto handleMethodArgumentNotValidException( MethodArgumentNotValidException validationException, Locale locale) { ... /* map validationException.getBindingResult().getFieldErrors() * to ValidationErrorDto (custom class) */ return validationErrorDto; } }

Por cierto: no es necesario registrar GlobalMethodHandlerExeptionResolver porque Spring HandlerExceptionResolver automáticamente todos los beans que implementan HandlerExceptionResolver para las resoluciones de excepciones. Así que un simple <bean class="GlobalMethodHandlerExeptionResolver"/> es suficiente.