一, springboot 的异常处理
首先, 说一下, Springboot 支持两种方式的默认处理机制: 一种是客户端的 (基于接口), 一种是网页的. 说白了就是根据请求的时候 Accept 的类型去进行异常的处理, 在 html 中, Accept 的类型是 text/HTML, 而基于接口去访问的话, Accept 的类型是 /
我们可以截图来看一下
网页中的请求
接口中的请求
然后, 在这两种请求方式的基础上, 我来随便写一个我后台中没有定义的一个接口 (当然了, 肯定会报 404)
网页 404
可以看到, 在基于 springboot 的项目中, 如果访问了一个未知的资源路径, 系统会自动跳到一个 Whitelabel Error Page 的 HTML 页面中
接口请求的 error
而在模拟客户端请求的时候, 会返回一串 JSON, 里面展示了错误的一些状态等信息. 那么, 在 springboot 中是如何区分这两种请求方式的呢?
二, 剖析源码
源码: BasicErrorController
- @RequestMapping(
- produces = {"text/html"}
- )
- public ModelAndView errorHtml(HttpServletRequest request, HttpServletResponse response) {
- HttpStatus status = this.getStatus(request);
- Map<String, Object> model = Collections.unmodifiableMap(this.getErrorAttributes(request, this.isIncludeStackTrace(request, MediaType.TEXT_HTML)));
- response.setStatus(status.value());
- ModelAndView modelAndView = this.resolveErrorView(request, response, status, model);
- return modelAndView == null ? new ModelAndView("error", model) : modelAndView;
- }
- @RequestMapping
- @ResponseBody
- public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) {
- Map<String, Object> body = this.getErrorAttributes(request, this.isIncludeStackTrace(request, MediaType.ALL));
- HttpStatus status = this.getStatus(request);
- return new ResponseEntity(body, status);
- }
可以看到, 在 BasicErrorController 中, 有两个重载的方法, 一个是返回一个 modelAndView, 一个是返回一个 map, 而这两个异常处理的区别就是, 在返回 modelAndView 的那个方法上边, 匹配了 produces ="text/html", 但是在返回 map 的方法中则没有进行详细的指定, 由此我们可以大致了解 boot 的默认异常处理机制
springboot 异常处理源码
三, 自定义异常
1. 出现问题自动跳转到固定页面
我先来演示一遍效果, 同样, 我访问一个我系统后台没有定义的一个接口 (404 错误), 然后通过浏览器访问
404 错误页面
可以看到, 访问一个不存在的接口资源, 页面跳转到了一个我自己定义的一个页面
- <!DOCTYPE HTML>
- <HTML>
- <head>
- <meta charset="UTF-8">
- <title>404</title>
- </head>
- <body>
404 您所访问的页面不存在...
</body>
</HTML>
那么这个是怎么出现的呢?
我们知道在 boot 的项目结构中, 在 src->main->resources 目录下是配置我们的配置文件的, 在此目录下我们再新建一个文件夹 resources, 然后在新的文件夹下面再新建一个文件夹, 命名为 error,(注意目录结构), 然后我们就可以在 error 目录下新建 HTML, 想让什么错误走什么样的 HTML 就去写, 就比如我之前已经预测出我访问接口会报 404, 那么我就可以创建 404.HTML, 以此类推还可以新建 500 之类的错误
错误处理
2. 客户端模拟访问异常处理
那么在客户端模拟访问接口的时候, 并不是返回页面, boot 默认返回的是一个 map, 而且可读性很差, 那么我们怎么办呢? 请看下面
四, 自定义异常
1, 新建自定义异常
我们在实际开发过程中, 为了提高系统代码的质量, 我们会新建很多自定义异常, 比如我创建一个自定义异常 UserNotExitException, 然后为了方便阅读, 我会定义一个接口, 去传递一个 id, 出现异常的时候将 id 传给这个异常, 我们来看代码
自定义异常代码:
- package com.tinner.exception;
- import com.sun.javafx.iio.iOS.IosDescriptor;
- public class UserNotExitException extends RuntimeException {
- public UserNotExitException( Integer id){
- super("user not exit");
- this.id = id;
- }
- private Integer id;
- public Integer getId() {
- return id;
- }
- public void setId(Integer id) {
- this.id = id;
- }
- }
接口代码:
- @GetMapping("/getUser/{id:\\d+}")
- public User getUser(@PathVariable Integer id){
- throw new UserNotExitException(id);
- }
为了方便我直接抛出了这个异常, 而且我在抛出这个异常的时候将父类的构造方法重写, 返回一个 user not exit 的消息, 我们来看接口访问:
自定义异常
我们可以看到返回的 message 改变了, 但是这个 JSON 的可读性还是有些差, 下面我将会介绍一个自己定义的返回对象
2, 自定义接口返回对象
我来新建一个 ControllerExceptionHandler 这个类, 这个类中必须加入注解 @ControllerAdvice, 我定义一个 handleUserNotExitException 方法, 返回一个 map, 传参必须传递 UserNotExitException 这个异常 (也就是说, 你想处理什么异常, 就必须将这个异常传递到这个方法中), 在这个异常中我提前定义了一个成员变量 id, 我想将这个 id 返回给客户端, 同时告诉它一些详细信息, 那么我们可以在 map 中 put 相关信息, 代码如下:
- @ControllerAdvice
- public class ControllerExceptionHandler {
- @ExceptionHandler(UserNotExitException.class)
- @ResponseBody
- @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
- public Map<String,Object> handleUserNotExitException(UserNotExitException ex){
- Map<String,Object> map = new HashedMap();
- map.put("id",ex.getId());
- map.put("message",ex.getMessage());
- return map;
- }
- }
我们可以看到在这个方法上边我写了三个注解, 第一个注解是告诉框架这个方法是用来处理什么异常的; 第二个注解是为了接口返回的; 第三个注解是告诉框架在服务器发生什么错误的时候来走这个方法 (HttpStatus.INTERNAL_SERVER_ERROR 是服务器错误的时候 500)
然后我们重新访问一遍这个接口
处理自定义异常返回
来源: http://www.jianshu.com/p/40096e2772f7