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.