Spring MVC 高级技术 - 处理异常和控制器通知

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/yzy199391/article/details/89945904


Spring提供多种方法将异常转化为响应:

  • 特定的Spring异常会自动映射为指定的HTTP状态码;
  • 异常上可以添加@ResponseStatus注解,从而将其映射为某一个状态码;
  • 在方法上可以添加ExceptionHandler注解,使其用来处理异常;

将异常映射为HTTP状态码

Spring异常自动映射

Spring异常 HTTP状态码
BindException 400-Bad Request
ConversionNotSupportedException 500-Internal Error
HttpMediaTypeNotAcceptableException 406-Not Acceptable
HttpMediaTypeNotSupportedException 415-Unsupported Media Type
HttpMessageNotReadableException 400-Bad Request
HttpMessageNotWritableException 500-Internal Server Error
HttpRequestMethodNotSupportedException 405-Method Not Allow
MethodArgumentNotValidException 400-Bad Request
MissingServletRequestParameterException 400-Bad Request
MissingServletRequestPartException 400-Bad Request
NoSuchRequestHandlingMethodException 404-Not Found
TypeMismatchException 400-Bad Request

上述异常由Spring自身抛出,作为DispatcherServlet处理过程中或执行校验时出现问题的结果。

@ResponseStatus

@ResponseStatus(value = HttpStatus.NOT_FOUND, reason = "service not found")
public class MyException extends RuntimeException{
}

@RestController
public class ExceptionController {
    @GetMapping("exception")
    private void exceptionTest(@RequestParam String param){
        if("no".equals(param)){
            throw new MyException();
        }
    }
}

上述代码中使用@ResponseStatus注解,在程序抛出MyException时,指定响应具有404状态码。

@ExceptionHandler

处理异常希望响应中不仅包含状态码,还需要包含产生的错误,仅将异常映射为HTTP状态码就不够了。此时,需要按照处理请求的方式来处理异常。

@PostMapping("exception/handler")
    public String exceptionHandler(@Nullable FileMode fileMode){
        try {
            fileMode.getName().toLowerCase();
        } catch (Exception e) {
            return "false";
        }
        return fileMode.getName();
    }

上述代码中,异常处理代码和业务逻辑代码混在了一起。我们可以利用@ExceptionHandler将异常处理逻辑抽取出来。

@PostMapping("exception/handler")
    public String exceptionHandler(@Nullable FileMode fileMode){
        fileMode.getName().toLowerCase();
        return fileMode.getName();
    }

    @ExceptionHandler(Exception.class)
    public String handlerException(){
        return "false";
    }

将业务逻辑和异常处理分开,代码结构更加清晰。

⚠️:@ExceptionHandler标注的方法可以处理同一个控制器中所有的处理器方法抛出的异常;若要处理所有控制器方法抛出的异常,需要将@ExceptionHandler标注的方法定义到控制器通知类中。

为控制器添加通知(@ControllerAdvice)

控制器通知类使用@ControllerAdvice标注,会包含一个或多个如下类型方法:

  • @ExceptionHandler注解标注的方法:
    全局处理控制器中异常。
  • @InitBinder注解标注的方法:
    设置WebDataBinder,用来自动绑定前台请求参数到Model中。
  • @ModelAttribute注解标注的方法:
    绑定键值对到Model里,此处是让全局的@RequestMapping都能获取在此处设置的键值对。

在通知类中,上述方法会运用到所有controller中带有@RequestMapping的方法上。

@ControllerAdvice本身带有@Component。

使用样例如下,很好地体现了通知类中各个注解的作用:

@ControllerAdvice
public class GlobleControllerAdvice {
    //@ExceptionHandler参数中指定处理的异常或在方法参数中指定
    @ExceptionHandler(Exception.class)
    public ModelAndView exceptionHandler(Exception e, WebRequest request){
        ModelAndView modelAndView = new ModelAndView("error");
        modelAndView.addObject("errorMessage", e.getMessage());
        return modelAndView;
    }

    /**
     * 在执行控制器之前执行,初始化数据模型,键值对可以用ModelMap接收
     * @param model
     */
    @ModelAttribute
    public void addAttribute(Model model){
        model.addAttribute("globleModelAttr", "gma");
    }

    /**
     * 控制器参数转换之前被执行的代码
     * @param webDataBinder Spring MVC会自动生成的参数
     */
    @InitBinder
    public void initBinder(WebDataBinder webDataBinder){
        //自定义日期编辑器
        CustomDateEditor dateEditor = new CustomDateEditor(new SimpleDateFormat("yyyy-MM-dd"), false);
        webDataBinder.registerCustomEditor(Date.class, dateEditor);
    }
}

上述代码为一个ControllerAdvice示例,调用代码如下:

@RestController
public class ExceptionController {

    @GetMapping("/exception")
    public ModelAndView testExceptionHandler(Date date, ModelMap modelMap){
        System.out.println(modelMap.get("globleModelAttr"));
        System.out.println(date);
        throw new NullPointerException("yanzy test error message");
    }
}

错误页面定义:

<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <title>错误页面</title>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
    <!-- jquery -->
    <script type="text/javascript" th:src="@{/js/jquery.min.js}"></script>
</head>
<body>
<h1 th:text="*{errorMessage}"></h1>
<h1 th:text="*{globleModelAttr}"></h1>
</body>
</html>

请求此服务,仅带有参数"?date=2019-05-08",响应结果为:

<!DOCTYPE HTML>
<html>
    <head>
        <title>错误页面</title>
        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
        <!-- jquery -->
        <script type="text/javascript" src="/js/jquery.min.js"></script>
    </head>
    <body>
        <h1>yanzy test error message</h1>
        <h1></h1>
    </body>
</html>

控制台打印信息:

gma
Wed May 08 00:00:00 CST 2019

多个@ExceptionHandler共存时优先级

根据声明的异常匹配度进行优先级排序。

注解注释的方法可以包含下列参数:
在这里插入图片描述

返回值可以为下列类型:
在这里插入图片描述

⚠️:advice中的@ExceptionHandler优先级低于当前Controller中定义的@ExceptionHandler

猜你喜欢

转载自blog.csdn.net/yzy199391/article/details/89945904
今日推荐