SpringMvc 你该知道如何在HandlerExceptionResolver中获取Model
来源:互联网 发布:网络通讯测试软件 编辑:程序博客网 时间:2024/06/15 22:24
在项目开发中,我们通常通过参数的形式注入Model对象,如:
@RequestMapping("/demo") public String demo(Model model) { model.addAttribute("message", "我是你的message!!!"); // HandlerMethodArgumentResolver throw new IllegalArgumentException("你就错了!!"); }
直接通过返回String来指定需要返回的View,然后在页面上直接可以访问Model对象的值了。
为了全局统对异常处理,通常我们还有个全局的异常处理中心:
需要实现HandlerExceptionResolver接口,具体配置的话这里就不赘述了。在异常处理中心,统一返回异常消息,这里假设返回的是json信息:
BaseResponse responseBean = new BaseResponse(Configuration.Status.STATUS_FAIL, filterErrorMsg ? defaultErrorMsg : ex.getMessage()); try { String message = mapper.writeValueAsString(responseBean); response.reset(); response.setContentType(contentType); response.getOutputStream().write(message.getBytes()); response.getOutputStream().flush(); } catch (Exception e) { log.error(e); } return new ModelAndView();
如果页面上只需要返回统一的错误信息,那么这个方式非常适合,没毛病。但是如果想在页面上回显消息呢?比如管理员修改用户信息,此时用户也在修改用户信息。管理员提交修改后,用户再提交修改就发生了并发错误,目前我们的系统是采用统一的异常抛出处理。只要抛出ConcurrentException就表示发生了并发错误,需要用户刷新页面重试。
发生错误后,当然要把用户之前的信息回显出来了,要不然用辛苦写了那么多信息被丢弃了,体验多不好。在页面上只要获取Model的信息显示出来就好了。
但是SpringMvc在异常情况下,并没有提供获取Model对象的方法
public interface HandlerExceptionResolver { /** * Try to resolve the given exception that got thrown during handler execution, * returning a {@link ModelAndView} that represents a specific error page if appropriate. * <p>The returned {@code ModelAndView} may be {@linkplain ModelAndView#isEmpty() empty} * to indicate that the exception has been resolved successfully but that no view * should be rendered, for instance by setting a status code. * @param request current HTTP request * @param response current HTTP response * @param handler the executed handler, or {@code null} if none chosen at the * time of the exception (for example, if multipart resolution failed) * @param ex the exception that got thrown during handler execution * @return a corresponding {@code ModelAndView} to forward to, or {@code null} * for default processing */ ModelAndView resolveException( HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex);}
因为数据是通过参数对象传入的,所以有handler也拿不到model对象的数据。
对于解决方法
1、AOP肯定是可以的,在Handler调用之前,把Model对象保存下来,当方法调用抛出异常后,将Model信息保存到Request对象上,这样在HandlerExceptionResolver中就可以获取到Model的信息了。
2、通过SpringMvc的HandlerMethodArgumentResolver来解决,当设置参数时,把Model对象保存到Request上,这样在HandlerExceptionResolver也可以移获取到了。
下面来说说第二中实现方式:
首先项目是集成了SpringBoot的,在WebMvcConfigurerAdapter中,有addArgumentResolvers方法
@Override public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) { // argumentResolvers.add(new MyArgumentsResolver()); // argumentResolvers.add(0, new CacheableModelMethodProcessor()); }
通过此方法可以自定义参数处理,但是仅限于自定义的参数,比如你自己添加了Student类型的参数。从注释中可以得知,对于SpringMvc内置提供的参数是无法覆盖的。
Add resolvers to support custom controller method argument types. This does not override the built-in support for resolving handler method arguments. To customize the built-in support for argument resolution, configure RequestMappingHandlerAdapter directly. This implementation is empty.
要覆盖默认的参数处理,需要通过RequestMappingHandlerAdapter进行处理。
首先,自定义一个HandlerMethodArgumentResolver,专门对Model对象进行处理:
public class CacheableModelMethodProcessor implements HandlerMethodArgumentResolver { public static final String KEY_MODEL = "com.cml.springboot.framework.argument.CacheableModelMethodProcessor.KEY_MODEL"; @Override public boolean supportsParameter(MethodParameter parameter) { return Model.class.isAssignableFrom(parameter.getParameterType()); } @Override public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception { ModelMap map = mavContainer.getModel(); webRequest.setAttribute(KEY_MODEL, map, NativeWebRequest.SCOPE_REQUEST); return map; }}
每次处理后,将Model对象保存到Request中。
其次,注册此HandlerMethodArgumentResolver,在Bean初始化完毕后,进行注册:
@Componentpublic class CustomModelArgumentResolverConfiguration { @Autowired private RequestMappingHandlerAdapter requestMappingHandlerAdapter; /** * 覆盖系统默认的处理器 */ @PostConstruct public void afterProperties() { List<HandlerMethodArgumentResolver> argumentResolvers = new ArrayList<>(requestMappingHandlerAdapter.getArgumentResolvers()); argumentResolvers.add(0, new CacheableModelMethodProcessor()); requestMappingHandlerAdapter.setArgumentResolvers(argumentResolvers); }}
或
@Configurationpublic class CustomModelArgumentResolverConfiguration { @Bean public RequestMappingHandlerAdapter adapter(RequestMappingHandlerAdapter adapter) { List<HandlerMethodArgumentResolver> argumentResolvers = new ArrayList<>(adapter.getArgumentResolvers()); argumentResolvers.add(0, new CacheableModelMethodProcessor()); adapter.setArgumentResolvers(argumentResolvers); return adapter; }}
以上都能实现覆盖默认的HandlerMethodArgumentResolver,推荐使用第一种方法。
注入自定义的HandlerMethodArgumentResolver后,只要在Controller中添加Model对象的参数,这样在HandlerExceptionResolver中就可以从Request中获取Model对象了:
request.getAttribute(CacheableModelMethodProcessor.KEY_MODEL)
至于ModelAndView参数,也是同理。
以上集成后就可以在HandlerExceptionResolver中获取到Model,在返回ModelAndView中设置此model,这样在页面上就可以获取到要回显的信息了。
当然可能有疑问了,既然能获取到Request对象,我直接从Request中获取参数不就好了吗?当然,这样是可以的,但是如果在Controller中你有自定义消息呢?比如我就单纯的添加了处理消息:
@RequestMapping("/demo") public String demo(Model model) { model.addAttribute("message", "我是你的message!!!"); // HandlerMethodArgumentResolver throw new IllegalArgumentException("你就错了!!"); }
这中情况无法从Request中获取到了。
当然,数据不一定要存到Model对象中,可以放到Request,或ThreadLocal中…各种方法都有,只要能实现。都是可以的。这里只是提供了一种觉得简便的方法。
至于在Spring项目中而不是SpringBoot使用,原理是相同的,只要根据步骤实现即可。
SpringBootLean 是对springboot学习与研究项目,是根据实际项目的形式对进行配置与处理,欢迎star与fork。
[oschina 地址]
http://git.oschina.net/cmlbeliever/SpringBootLearning
[github 地址]
https://github.com/cmlbeliever/SpringBootLearning
- SpringMvc 你该知道如何在HandlerExceptionResolver中获取Model
- 如何在SpringMVC中获取request对象
- 如何在SpringMVC中获取request对象
- 如何在SpringMVC中获取request对象
- 如何在SpringMVC中获取request对象
- 如何在SpringMVC中获取request对象
- 如何在SpringMVC中获取request对象
- 如何在SpringMVC中获取request对象
- 如何在SpringMVC中获取request对象
- 如何在SpringMVC中获取request对象
- 如何在SpringMVC中获取request对象
- 如何在SpringMVC中获取request对象
- 如何在SpringMVC中获取request对象
- 如何在SpringMVC中获取request对象
- 如何在SpringMVC中获取request对象
- 如何在批处理文件中获取该批处理文件所处位置?
- 如何在 Linux 终端中知道你的公有 IP
- 如何在 Linux 终端中知道你的公有 IP
- AngularJS实时监听Html控件状态(值)变化
- 归并排序(java实现)
- JavaScript 页面跳转并传值,解析url成对象
- GraphicsLayer 与 FeatureLayer
- spark2.x写入数据到ElasticSearch5.X集群
- SpringMvc 你该知道如何在HandlerExceptionResolver中获取Model
- leetcode#112. Path Sum
- adb 拷贝文件没有权限
- very Good
- C++排序算法之基数排序
- undefined 和 null
- 简单理解Socket
- iOS编程基础-Swift(五)-流程控制与其他(序)
- 51nod 1179 最大的最大公约数