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

相关推荐
2601_966949653 小时前
如何封装 qd.klines.batch(),让批量 K 线查询函数在多个策略中复用?
开发语言·python·数据分析·pandas·量化交易·股票数据·quantdash
Mikko73 小时前
Redis 连接数满了服务端有错误信息吗?连接数不增加却命令超时?6.2 实测连接池、超时与大 key 怎么判定
java·spring boot·redis·后端
.Bryce.4 小时前
从零到一开发技术名词大全:从需求分析到 AI Agent 工程
java·网络·人工智能·agent·需求分析·agent评测
SL_staff4 小时前
JVS-APS的7个刚性约束设计:从无限产能幻觉到可执行排产的工程实践
java·spring cloud·全栈
神仙别闹5 小时前
基于Python实现英词语自动对齐系统
开发语言·python
akinggw5 小时前
libagentcore - 一个基于c++的AI多agent系统升级到2.0版本
开发语言·jvm·c++
代码山河5 小时前
基本类型转换:自动类型转换与强制类型转换
java·学习·架构·教程·面向对象·项目
老歌老听老掉牙5 小时前
圆柱端面四平面交线及其在yoz面投影的几何解析与 Python 实现
开发语言·python·平面
旖旎夜光6 小时前
【C++ 基础入门】命名空间、输入输出、缺省参数与函数重载
开发语言·c++·笔记·学习
俩个逗号。。6 小时前
Kotlin flow 详解
android·开发语言·kotlin