从ControllerAdvice的ExceptionHandler渲染视图时,能否触发其他ControllerAdvice?
Thymeleaf升级到3.1.0后移除了#httpServletRequest属性,我写了一个标注@ControllerAdvice的UtilityProvider类,通过@ModelAttribute("requestUri")把HttpServletRequest的requestURI加入模型:
@ControllerAdvice class UtilityProvider { @ModelAttribute("requestUri") fun requestUri(request: HttpServletRequest): String { return request.requestURI } }
这个类对普通请求都生效,但当另一个标注@ControllerAdvice的GlobalExceptionHandlerAdvice类里的@ExceptionHandler处理异常并返回视图时,requestUri属性没被加到模型里。GlobalExceptionHandlerAdvice代码如下:
@ControllerAdvice public class GlobalExceptionHandlerAdvice { @ExceptionHandler(value = Exception.class) public ModelAndView defaultErrorHandler(HttpServletRequest request, HttpServletResponse response, Exception e) { return new ModelAndView("error"); } }
我有两个疑问:
- 这是预期行为吗?
- 有没有办法让
UtilityProvider的@ControllerAdvice也作用于异常处理器渲染的视图?
我试过用@Ordered注解调整两个@ControllerAdvice的执行顺序,但没效果;目前已经在异常处理器里手动添加该属性临时解决(代码如下),但不想用这种方式:
@ControllerAdvice public class GlobalExceptionHandlerAdvice { @ExceptionHandler(value = Exception.class) public ModelAndView defaultErrorHandler(HttpServletRequest request, HttpServletResponse response, Exception e) { Map<String, Object> model = new HashMap<>(); model.put("requestUri", request.getRequestURI()); return new ModelAndView("error", model); } }
1. 这是预期行为吗?
是的,这属于Spring MVC的预期行为。当@ExceptionHandler方法直接返回ModelAndView时,Spring MVC不会自动触发其他@ControllerAdvice类中的@ModelAttribute方法——异常处理流程的模型填充逻辑和正常请求流程有差异,默认只会处理当前@ControllerAdvice内的@ModelAttribute,不会跨类触发其他全局@ModelAttribute。
2. 如何让UtilityProvider的@ModelAttribute作用于异常视图?
有两种更优雅的方式解决:
方式一:让异常处理器返回视图名称而非ModelAndView
修改GlobalExceptionHandlerAdvice的方法,直接返回字符串类型的视图名,这样Spring MVC会走完整的视图渲染流程,包括触发所有@ControllerAdvice的@ModelAttribute:
@ControllerAdvice public class GlobalExceptionHandlerAdvice { @ExceptionHandler(value = Exception.class) public String defaultErrorHandler(HttpServletRequest request, HttpServletResponse response, Exception e) { return "error"; } }
方式二:在异常处理器中注入Model参数并复用全局逻辑
如果必须返回ModelAndView,可以在方法参数中加入Model,Spring会自动将全局@ModelAttribute填充到这个Model里,再用它构建ModelAndView:
@ControllerAdvice public class GlobalExceptionHandlerAdvice { @ExceptionHandler(value = Exception.class) public ModelAndView defaultErrorHandler(HttpServletRequest request, HttpServletResponse response, Exception e, Model model) { // 此时model已经包含UtilityProvider中添加的requestUri return new ModelAndView("error", model.asMap()); } }
补充:关于@Ordered无效的原因
@Ordered注解只控制@ControllerAdvice中同类型方法的执行顺序(比如多个@ExceptionHandler的执行优先级),但无法改变异常处理流程中是否触发全局@ModelAttribute的规则,所以调整顺序不会解决问题。
内容的提问来源于stack exchange,提问作者peach

