快速入门 Spring Boot 拦截器、统一响应格式和全局异常处理

一、拦截器

1.1、什么是拦截器?

拦截器是 Spring 框架的核心功能之一,主要用于拦截用户请求,在目标方法执行前后根据业务需求执行预设的代码逻辑。如下图所示,在用户请求到达服务器之前,拦截器会对发起请求的客户端进行身份认证和权限验证,从而有效防止未登录用户向无权限的界面发送请求。

1.2、拦截器的使用

1.2.1、自定义拦截器
java 复制代码
@Configuration
public class LoginInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("方法调用的路径是:"+request.getRequestURI());
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("方法已完成");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("视图渲染也完成!");
    }
}

preHandle()方法:在目标方法执行前运行。返回 true 时继续执行后续流程,返回false则终止后续操作

postHandle()方法:在目标方法执行后立即运行

afterCompletion()方法:在视图渲染完成后执行

1.2.2、配置拦截器

我们需要先实现 WebMvcConfigurer 接口,并重写其中的 addInterceptors 方法。

java 复制代码
@Component
public class WebConfig implements WebMvcConfigurer{
    @Autowired
    LoginInterceptor loginInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(loginInterceptor)
                .addPathPatterns("/**")
                .excludePathPatterns("/login");
    }
}

上述这段代码中:

其他拦截路径配置

1.2.3、测试

让我们先分析自定义拦截器中三个重写方法的执行时序及其作用效果

对于一个正常的请求,如果不拦截的情况下:会依次输入上述三个方法

java 复制代码
@RestController
public class Test {
    @RequestMapping("/login")
    public String login(){
        return "login"+"路径登录成功";
    }
    @RequestMapping("/getUser")
    public String getUser(){
        return "getUser";
    }
}

指定两个路径,并添加拦截路径后(见目录 1.2.2):

访问未放行的路径: 127.0.0.1:8080/getUser 可看到无任何返回

访问已放行的路径: 127.0.0.1:8080/login 可以看到代码中写好的结果

二、统一响应格式

2.1、为什么要统一响应格式?

当请求通过拦截器发送至服务器后,服务器会对请求报文进行处理并返回响应报文。这个交互过程涉及前端向后端发送参数,以及后端向前端返回处理结果。当前的主要问题在于后端返回的参数类型不固定,导致前端难以处理。因此,我们需要通过统一响应机制来规范前后端之间的数据格式。

对于统一响应格式的标准设计,一个包含:

状态码:告诉前端请求处理结果

消息:可读的描述消息(包括出现什么异常、响应成功等等)

数据:真正的业务数据

时间戳:方便调试和排除问题

2.2、实现统一响应

采用 @ControllerAdvice 和 ResponseBodyAdvice实现统一数据返回格式

@ControllerAdvice 注解用于定义控制器通知类

java 复制代码
@Data
@AllArgsConstructor
@NoArgsConstructor
@ControllerAdvice
public class Result<T> {
    private Integer code;
    private String message;
    private T data;

    public static <T> Result<T> success(T data) {
        return new Result<>(200, "success", data);
    }

    public static <T> Result<T> error(String message) {
        return new Result<>(500, message, null);
    }
}
java 复制代码
@ControllerAdvice
//统一返回响应
public class ResponseAdvice implements ResponseBodyAdvice<Object> {
    private static ObjectMapper mapper = new ObjectMapper();
    @Override
    public boolean supports(MethodParameter returnType, Class converterType) {
        return true;
    }
    @SneakyThrows
    @Override
    public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
        if(body instanceof Result){
            return body;
        }
        if(body instanceof String){
            return mapper.writeValueAsString(Result.success(body));
        }
        return Result.success(body);
    }
}

上述代码中,使用 ObjectMapper 主要是用来对 String 类型进行 JSON 格式化,如果传递的是String类型,那么会出现类型转换错误的情况,详情见下方优秀博主的链接解释:Spring统一数据返回格式处理String类型出错解析_beforebodywrite中类型是string报错-CSDN博客

另外对于本就是Result类型的数据,就无需二次统一响应格式了,故加入两个判断条件

三、全局异常处理

3.1、快速上手

统一异常处理通过 @ControllerAdvice 和@ExceptionHandler组合实现。 @ControllerAdvice 标记控制器通知类,@ExceptionHandler用于定义异常处理方法。两者配合使用可在异常发生时触发指定的处理逻辑。

java 复制代码
@RestControllerAdvice
public class ErrorAdvice {
    @ExceptionHandler(Exception.class)
    public Result<?> handler(Exception e){
        return Result.fail(e.getMessage());
    }

    @ExceptionHandler(NullPointerException.class)  // 明确指定异常类型
    public Result<?> handler(NullPointerException e){
        return Result.fail("发生空指针异常:" + e.getMessage());
    }

    @ExceptionHandler(ArithmeticException.class)  // 明确指定异常类型
    public Result<?> handler(ArithmeticException e){
        return Result.fail("发生算数异常:" + e.getMessage());
    }
}

在下方做三个接口的示例,第一个是无错版本,第二个对于不同的报错类型

java 复制代码
@RestController
public class UserController {
    @RequestMapping(value = "aa",produces = "application/json")
    public String aa() {
        return "aa";
    }
    @RequestMapping(value = "bb",produces = "application/json")
    public String bb() {
        int a=10/0;
        return "bb";
    }
    @RequestMapping(value = "cc",produces = "application/json")
    public String cc() {
        String s=null;
        System.out.println(s.length());
        return "cc";
    }
}

3.2、测试

下面我们将依次访三个URL:

相关推荐
两年半的个人练习生^_^10 小时前
JMM 进阶:彻底理解 CAS 实现原理
java·开发语言
wuminyu10 小时前
Java锁机制之park和unpark源码剖析
java·linux·c语言·jvm·c++
半个烧饼不加肉10 小时前
JS 底层探究-- 事件循环
开发语言·前端·javascript
W_LuYi18510 小时前
手撸极简zkEVM验证器:RISC-V电路实践
java·risc-v
asdfg125896310 小时前
C 语言中产生伪随机数的标准做法
c语言·开发语言
AI人工智能+电脑小能手10 小时前
【大白话说Java面试题 第102题】【并发篇】第2题:volatile 能否保证线程安全?
java·安全·面试
KobeSacre10 小时前
JUC 概述
java·开发语言
小bo波11 小时前
形式化方法 × UML
java·软件工程·uml·面向对象·形式化方法·tla+
Jun62611 小时前
QT(2)-通过管道关联CMD
开发语言·qt·命令模式
就叫_这个吧11 小时前
IDEA中Javaweb项目创建+servlet,实现简单的信息录入获取
java·servlet·intellij-idea·web