SpringBoot统一异常处理和统一返回格式

上篇博客我们讲解了使用AOP来进行统一的用户登录判断,其实像这种功能统一且使用较多的地方,都可以用AOP来处理,除了统⼀的⽤户登录判断之外,AOP 还可以实现:

  • 统⼀⽇志记录
  • 统⼀⽅法执⾏时间统计(在性能优化阶段,监控流量,接口的响应时间等甚至每个方法的响应时间,为整个项目的性能进行优化)
  • 统⼀的返回格式设置 (对于接口的返回格式,基本上都是code,message,data)
  • 统⼀的异常处理
  • 事务的开启和提交等

1.统一异常处理

我们写一个类来测试一下:

package com.example.springaop.controller;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
//@RequestMapping("/ex")
public class ExceptionController {
    @RequestMapping("/test1")
    public boolean test1() {
        int a = 10/0;
        return true;
    }

    @RequestMapping("/test2")
    public boolean test2() {
        String str = null;
        System.out.println(str.length());
        return true;
    }

    @RequestMapping("/test3")
    public boolean test3() {
        throw new RuntimeException("测试运行时异常");
    }
}

运行后去浏览器访问对于的url测试:

SpringBoot统一异常处理和统一返回格式_第1张图片

 

 

可以在控制台看见对应的异常信息如果不进行统一异常处理的话,那么有些浏览器可能会将异常清楚的告知用户 

统⼀异常处理使⽤的是 @ControllerAdvice + @ExceptionHandler 来实现的,@ControllerAdvice 表示控制器通知类,@ExceptionHandler 是异常处理器,两个结合表示当出现异常的时候执⾏某个通知,也就是执⾏某个⽅法事件,具体实现代码如下:
package com.example.springaop.config;

import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;

import java.util.HashMap;

@ControllerAdvice// 控制器的通知,交给Spring管理
public class ErrorHandler {
    @ResponseBody// 表示返回的数据不是视图
    @ExceptionHandler
    public Object error (Exception e) {// 根据我的方法参数来捕获异常,这里捕获的就是Exception异常
        HashMap result = new HashMap<>();
        result.put("success",0);
        result.put("code",-1);
        result.put("msg","内部异常");
        return result;
    }
}
  • ⽅法名和返回值可以⾃定义,其中最重要的是 @ExceptionHandler(Exception.class) 注解,括号里面的内容可以不写
  • 当有多个异常通知时,匹配顺序为当前类及其⼦类向上依次匹配
我们去浏览器测试一下异常
SpringBoot统一异常处理和统一返回格式_第2张图片

SpringBoot统一异常处理和统一返回格式_第3张图片 

可以看到这些异常返回了相应的结果 

我们再来写一个捕获算数异常和空指针异常统一处理的方法

    @ResponseBody// 表示返回的数据不是视图
    @ExceptionHandler
    public Object error (ArithmeticException e) {// 根据我的方法参数来捕获异常,这里捕获的就是算数ArithmeticException异常
        HashMap result = new HashMap<>();
        result.put("success",0);
        result.put("code",-2);
        result.put("msg","ArithmeticException。。。");
        return result;
    }

    @ResponseBody// 表示返回的数据不是视图
    @ExceptionHandler
    public Object error (NullPointerException e) {// 根据我的方法参数来捕获异常,这里捕获的就是空指针异常
        HashMap result = new HashMap<>();
        result.put("success",0);
        result.put("code",-3);
        result.put("msg","NullPointerException。。。");
        return result;
    }

此时我们再来访问算数异常的url为test1,空指针异常为test2

SpringBoot统一异常处理和统一返回格式_第4张图片

SpringBoot统一异常处理和统一返回格式_第5张图片 

此时就返回了我们设置的算数异常和空指针异常应该返回的数据 

这里有一个需要注意的点就是,如果我们给ExceptionController加上了拦截器

SpringBoot统一异常处理和统一返回格式_第6张图片

 这样我们再去访问的时候发现:

SpringBoot统一异常处理和统一返回格式_第7张图片

SpringBoot统一异常处理和统一返回格式_第8张图片 

我们并未修改Exception Controller类中的任何代码,只是给他加了一个拦截器而已,那么这又是什么原因呢?

我们查看环绕通知的代码即可发现,不论是什么异常,经过环绕通知最后都变成了运行时异常

SpringBoot统一异常处理和统一返回格式_第9张图片

解决办法就是把这里修改一下,把连接点里面的异常直接抛出,就像下面这样:SpringBoot统一异常处理和统一返回格式_第10张图片

 此时我们再去浏览器测试:

SpringBoot统一异常处理和统一返回格式_第11张图片

SpringBoot统一异常处理和统一返回格式_第12张图片 

算数异常和空指针异常又正常显示了

代码之间经常会互相影响,出现错误后,一定要有耐心的寻找错误,相信自己一定可以找到的

2.统⼀的返回格式

1. 为什么需要统⼀数据返回格式

  • ⽅便前端程序员更好的接收和解析后端数据接⼝返回的数据。
  • 降低前端程序员和后端程序员的沟通成本,按照某个格式实现就⾏了,因为所有接⼝都是这样返回的。
  • 有利于项⽬统⼀数据的维护和修改。
  • 有利于后端技术部⻔的统⼀规范的标准制定,不会出现稀奇古怪的返回内容

2. 统⼀数据返回格式的实现

统⼀的数据返回格式可以使⽤ @ControllerAdvice + ResponseBodyAdvice 的⽅式实现,具体实现代码如下:

 

@ControllerAdvice
public class ResponseHandler implements ResponseBodyAdvice {

    /**
     * 内容是否需要重写(通过此⽅法可以选择性部分控制器和⽅法进⾏重写)
     * 返回 true 表示重写
     */
    @Override
    public boolean supports(MethodParameter returnType, Class converterType) {
        return true;
    }

    /**
     * ⽅法返回之前调⽤此⽅法
     */
    @Override
    public Object beforeBodyWrite(Object body, // 响应的正文内容
                                  MethodParameter returnType,
                                  MediaType selectedContentType,
                                  Class selectedConverterType,
                                  ServerHttpRequest request,
                                  ServerHttpResponse response) {
        // 封装
        HashMap result = new HashMap<>();
        result.put("success",1);
        result.put("data",body);
        result.put("errorMsg","");
        return result;
    }
}

此时我们在访问user类中的url:

/**
 * 用户需要调用的一些方法
 */
@Slf4j
@RestController
@RequestMapping("/user")
public class UserController {
    // 获取用户信息
    @RequestMapping("/get")
    public String getInfo() {
        log.info("获取信息...");
        return "获取信息";
    }

    // 注册
    @RequestMapping("/reg")
    public Boolean reg() {
        log.info("注册...");
        return true;
    }

    // 登录
    @RequestMapping("/log")
    public Boolean log(HttpServletRequest request, String username, String password) {
        log.info("login...");
        // 判断用户名和密R码,如果有任意一个为空,那么就不能登陆成功
        if (!StringUtils.hasLength(username) || !StringUtils.hasLength(password)) {
            return false;
        }
        // 此时判断用户名和密码是否正确
        // 假装判断一下
        if (!"admin".equals(username) || !"admin".equals(password)) {
            return false;
        }
        // 此时用户名和密码校验通过
        // 参数true表示,如果没有存放Session,那么需要创建一个Session来存放当前登录的用户
        HttpSession session = request.getSession(true);
        session.setAttribute("username",username);
        return true;
    }

}

SpringBoot统一异常处理和统一返回格式_第13张图片 

SpringBoot统一异常处理和统一返回格式_第14张图片

 SpringBoot统一异常处理和统一返回格式_第15张图片

 可以看到,我们访问get的url时出错了,因为它返回的数据是String类型

原因在这里可以看到:SpringBoot 使用 beforeBodyWrite 实现统一的接口返回类型_一梦喂马.的博客-CSDN博客

那么该如何解决呢?

再加一个判断方法,当它返回的数据为String类型时,手动按照Json的数据格式返回

SpringBoot统一异常处理和统一返回格式_第16张图片

 异常使用@SneakyThrows注解,重新去浏览器访问:

SpringBoot统一异常处理和统一返回格式_第17张图片

SpringBoot统一异常处理和统一返回格式_第18张图片

可以看到访问成功了,不论访问的是那个页面,成功与否,返回的数据都是我们刚才设置的统一格式

3.总结

  • 自定义拦截器使⽤ WebMvcConfigurer+ HandlerInterceptor来实现,
  • 统⼀异常处理使⽤ @ControllerAdvice + @ExceptionHandler 来实现,
  • 统⼀返回值处理使⽤@ControllerAdvice + ResponseBodyAdvice 来处理
下篇见~

 SpringBoot统一异常处理和统一返回格式_第19张图片

 

你可能感兴趣的:(springboot,java,开发语言)