一、拦截器
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:



