SpringMVC 异常处理.

Easter79
• 阅读 552

一、异常处理

    Spring提供了多种方式将异常转换为响应:

特定的Spring异常将会自动映射为指定的HTTP状态码。在默认情况下,Spring会将自身的一些异常自动转换为合适的状态码,从而反馈给客户端。实际上,如果没有出现任何映射的异常,响应都会带有500状态码。映射表如下:

SpringMVC 异常处理.

自定义异常上可以添加 @ResponseStatus 注解,从而将其映射为某一个HTTP状态码。尽管这些内置映射是很有用的,但是当我们的业务系统出现 RuntimeException 时,如果 Spring 找不到对应的内置映射,就默认是 500 的状态码,如果我们不想要 500 的状态码呢?怎么将我们自定义的Exception映射成想要的状态码呢?

/**
 * value 要匹配的异常状态码
 * reson 提示的异常原因
 */
@ResponseStatus(value = HttpStatus.NOT_FOUND,reason = "Own Exception")
public class OwnException extends RuntimeException {
}

在方法上可以添加 @ExceptionHandler 注解,使其用来处理异常。有很多时候,我们是不想把丑陋的报错页面直接展示给客户来看的,常见的做法是:搭建一个友好的页面,比如 error.jsp ,当发生异常的时候,返回这个页面给客户端。但是五花八门的处理器方法,如果每个地方都做这样的处理,我们的程序就会略显臃肿......Spring 为我们 提供了一种控制器通知(@ControllerAdvice),即:当所有控制器中带有 @RequestMapping 注解的方法上 出现异常的时候,就委托给这个类的 @ExceptionHandler 方法处理。

@ControllerAdvice
public class ExceptionHandle {
    /**
     * 当出现异常的时候,就返回error页面,当然可以多写几个ExceptionHandler 方法,细化你的异常处理
     * @return
     */
    @ExceptionHandler(value = Exception.class)
    public String handleException(){
        return "error";
    }
}

二、 @ControllerAdvice 不生效探究

    在Spring Boot 中尝试使用 "@ControllerAdvice + @ExceptionHandler" 作为全局的异常处理机制,却一直不生效,无论异常怎么抛出,始终到不了@ControllerAdvice ,在启动日志中,看到了如下信息:

2019-08-15 13:56:10.360  INFO 13540 --- [  restartedMain] .m.m.a.ExceptionHandlerExceptionResolver : Detected @ExceptionHandler methods in exceptionProcessor
2019-08-15 13:56:10.360  INFO 13540 --- [  restartedMain] .m.m.a.ExceptionHandlerExceptionResolver : Detected @ExceptionHandler methods in exceptionAdvice

    原来 @ExceptionHandler 已经在 exceptionProcessor 这个 Bean 中使用了,并且他的优先级更高,导致我们自定义的 @ExceptionHandler 一直不生效。怎么办呢?使用 @Order(Ordered.HIGHEST_PRECEDENCE) 提升 Bean 的优先级。

@Slf4j
@ControllerAdvice
@Order(Ordered.HIGHEST_PRECEDENCE)
public class ExceptionAdvice {

    @ExceptionHandler(Exception.class)
    @ResponseBody
    public ResponseData<Void> handleException(Exception ex) {
        log.error("admin route error.", ex);
        return ResponseData.buildErrorResponse(INTERNAL_SERVER_ERROR.value(), "服务器异常,请稍后再试", null);
    }
}

三、跨重定向请求传递数据

    在控制器方法返回的 String 视图名称中,如果以 "redirect:" 开头,那么这个 String 不是用来查找视图的,而是用来指导浏览器进行重定向的路径。有些时候,我们希望浏览器进行重定向后,有些数据是可以保留下来的,这听起来不可思议,但 SpringMVC 为我们提供了两种方案:

使用URL 模板以路径变量或查询参数的形式传递数据。这种方式将参数放在路径变量中传递,但是有一个缺点就是不能传递复杂的对象...

@RequestMapping(value = "/home",method = RequestMethod.GET)
    public String getHome(Model model){
        model.addAttribute("userName","userName");
        model.addAttribute("id",123);
        return "redirect:/home/{userName}";
    }

    像这样,如果最后的路径会被解析为 /home/userName?id=123

@RequestMapping(method = RequestMethod.POST)
public ModelAndView report(ModelMap map, HttpServletRequest request, @RequestBody AdminReportParam reportParam) throws JsonProcessingException {
    String redirectUrl = SYS_CONFIG.getReportHost() + StringUtils.remove(request.getRequestURI(), "/admin/v1");
    RedirectView redirectView = new RedirectView(redirectUrl, true, false, false);
    map.put("auth", true);
    map.put("param", JsonUtils.writeJsonStr(reportParam));
    return new ModelAndView(redirectView, map);
}

通过flash属性发送数据。如果要传递一些对象要怎么做呢?有一种方式就是在重定向前存在 session 中,在重定向后再从 session 中取出来,再清理 session 。实际上,这种方式是可行的,也是值得推荐的。而且以下介绍的这种(flash attribute)就是基于这个原理。

/**
     * 重定向前
     * @param model RedirectAttributes ,保证对象在重定向的过程中存活下来
     * @return
     */
    @RequestMapping(value = "/list",method = RequestMethod.GET)
    public String getList(RedirectAttributes model){
        model.addAttribute("show","show");
        List<String> list = new ArrayList<String>();
        list.add("str");
        model.addFlashAttribute("list",list); // flashAttribute
        return "redirect:{show}";
    }

    /**
     * 重定向后
     * @param model
     * @return
     */
    @RequestMapping(value = "/show",method = RequestMethod.GET)
    public String showList(Model model){
        System.out.println(model.containsAttribute("list")); //true
        return "home";
    }

SpringMVC 异常处理.

注意这种方式,只适合在同一个 web 应用中使用。

点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
待兔 待兔
5个月前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
Jacquelyn38 Jacquelyn38
3年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
Stella981 Stella981
3年前
HIVE 时间操作函数
日期函数UNIX时间戳转日期函数: from\_unixtime语法:   from\_unixtime(bigint unixtime\, string format\)返回值: string说明: 转化UNIX时间戳(从19700101 00:00:00 UTC到指定时间的秒数)到当前时区的时间格式举例:hive   selec
Wesley13 Wesley13
3年前
03.Android崩溃Crash库之ExceptionHandler分析
目录总结00.异常处理几个常用api01.UncaughtExceptionHandler02.Java线程处理异常分析03.Android中线程处理异常分析04.为何使用setDefaultUncaughtExceptionHandler前沿上一篇整体介绍了crash崩溃
Easter79 Easter79
3年前
Spring异常处理
@ExceptionHandler:统一处理某一类异常,从而能够减少代码重复率和复杂度@ControllerAdvice:异常集中处理,更好的使业务逻辑与异常处理剥离开@ResponseStatus:可以将某种异常映射为HTTP状态码@ControllerAdvicepublicclassExceptio
Stella981 Stella981
3年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
11个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这
Easter79
Easter79
Lv1
今生可爱与温柔,每一样都不能少。
文章
2.8k
粉丝
5
获赞
1.2k