快速入门 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:

相关推荐
鬼蛟2 小时前
Spring_MVC
java·spring·mvc
瓦哥架构实战2 小时前
CentOS 7 编译安装 Python 3.9 解决 SSL 模块缺失问题
开发语言·python
宵时待雨2 小时前
C++笔记归纳13:map & set
开发语言·数据结构·c++·笔记·算法
xiangpanf2 小时前
PHP与Vue:前后端技术深度对比
开发语言·vue.js·php
怀旧诚子3 小时前
timeshift之Fedora43设置,已在VM虚拟机验证,待真机验证。
java·服务器·数据库
1104.北光c°3 小时前
滑动窗口HotKey探测机制:让你的缓存TTL更智能
java·开发语言·笔记·程序人生·算法·滑动窗口·hotkey
for_ever_love__4 小时前
Objective-C学习 NSSet 和 NSMutableSet 功能详解
开发语言·学习·ios·objective-c
云原生指北6 小时前
GitHub Copilot SDK 入门:五分钟构建你的第一个 AI Agent
java
似水明俊德10 小时前
02-C#.Net-反射-面试题
开发语言·面试·职场和发展·c#·.net