文章目录
- [1. 拦截器](#1. 拦截器)
-
- [1.1 拦截路径](#1.1 拦截路径)
- [1.2 拦截器执行流程](#1.2 拦截器执行流程)
- [1.3 自定义拦截器](#1.3 自定义拦截器)
- [1.4 注册配置拦截器](#1.4 注册配置拦截器)
- [2. 统一数据返回格式](#2. 统一数据返回格式)
- [3. 统一异常处理](#3. 统一异常处理)
- [4. 总结](#4. 总结)
1. 拦截器
拦截器是Spring框架提供的核心功能之一, 主要用来拦截用户的请求, 在指定方法前后, 根据业务需要执行预先设定的代码
在拦截器当中,开发人员可以在应用程序中做一些通用性的操作, 比如通过拦截器来拦截前端发来的请求, 判断Session中是否有登录用户的信息. 如果有就可以放行, 如果没有就进行拦截.
拦截器的使用分两步:
- 自定义拦截器: 实现HandlerInterceptor接口,并重写其所有方法
- 注册配置拦截器: 实现WebMvcConfigurer接口,并重写addInterceptors方法
先简单介绍实现HandInterceptor接口要重写的方法:
- preHandle() :目标方法执行前执行, 返回true, 继续执行后续操作; 返回false, 中断后续操作.
- postHandle() : 目标方法执行后执行
- afterCompletion(): 视图渲染完毕后执行,最后执行(后端开发现在几乎不涉及视图, 暂不了解)
1.1 拦截路径
拦截路径是指我们定义的这个拦截器, 对哪些请求生效,
我们在注册配置拦截器的时候, 通过 addPathPatterns() 方法指定要拦截哪些请求. 也可以通过excludePathPatterns() 指定不拦截哪些请求, 具体使用会在后文介绍
常见拦截路径配置
/* :一级路径
/** :任意级路径
/book/* :/book下的一级路径
/book/** :/book下的任意级路径
1.2 拦截器执行流程
添加拦截器后, 执行Controller的方法之前, 请求会先被拦截器拦截住. 执行 preHandle() 方法,这个方法需要返回一个布尔类型的值.
如果返回true, 就表示放行本次操作, 继续访问controller中的方法.
如果返回false,则不会放行(controller中的方法也不会执行).
controller当中的方法执行完毕后,再回过来执行 postHandle() 这个方法以及afterCompletion() 方法,执行完毕之后,最终给浏览器响应数据
1.3 自定义拦截器
java
@Component
@Slf4j
public class LoginInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
HttpSession session = request.getSession(false);
if (session != null && session.getAttribute(Constants.SESSION_USER_KEY) != null) {
return true;
}
response.setStatus(401);
return false;
}
}
注意要加上@Component交给Spring管理
http状态码401: Unauthorized
未经过认证. 指示身份验证是必需的, 没有提供身份验证或身份验证失败. 如果请求已经包含授权凭据,那么401状态码表示不接受这些凭据
1.4 注册配置拦截器
java
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Autowired
private LoginInterceptor loginInterceptor;
//排除前端静态资源
private List<String> excludePaths = Arrays.asList(
"/user/login",
"/**/*.js",
"/**/*.css",
"/**/*.png",
"/**/*.html"
);
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 注册自定义拦截器对象
registry.addInterceptor(loginInterceptor)
.addPathPatterns("/**") //设置拦截器拦截的请求路径(/**表示拦截所有请求)
.excludePathPatterns(excludePaths); //设置拦截器排除拦截的路径
}
}
.excludePathPatterns的参数可以是Sting, 可也意识List<String>
2. 统一数据返回格式
统一的数据返回格式使用 @ControllerAdvice 和ResponseBodyAdvice 的方式实现:
@ControllerAdvice 表示控制器通知类
实现 ResponseBodyAdvice 接口, 并在类上添加@ControllerAdvice 注解
supports方法 : 判断是否要执行beforeBodyWrite方法. true为执行, false不执行. 通过该方法可以选择哪些类或哪些方法的response要进行处理, 其他的不进行处理
beforeBodyWrite方法: 对response方法进行具体操作处理
java
@ControllerAdvice
public class ResponseAdvice implements ResponseBodyAdvice {
@Override
public boolean supports(MethodParameter returnType, Class converterType) {
return false;
}
@Override
public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
return Result.success(body);
}
}
大多数项目确实不再单纯依赖它来做统一的返回类型封装, 而是结合泛型封装作为主力,ResponseBodyAdvice 作为辅助,
Controller 返回类型基本都是我们自定义的Result<T>, ResponseBodyAdvice 角色 只做有条件的增强(加密、脱敏、加头),不做统一格式包装
如果用ResponseBodyAdvice返回结果为String类型, 还要使用SpringBoot内置提供的Jackson来实现信息的序列化
java
@ControllerAdvice
public class ResponseAdvice implements ResponseBodyAdvice {
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) {
// 如果返回结果为String类型, 使用SpringBoot内置提供的Jackson来实现信息的序列化
if (body instanceof String){
return mapper.writeValueAsString(Result.success(body));
}
return Result.success(body);
}
}
3. 统一异常处理
统一异常处理使用的是 @ControllerAdvice + @ExceptionHandler 来实现的
@ControllerAdvice 表示控制器通知类 ;
@ExceptionHandler 是异常处理器 ;
两个结合表示当出现异常的时候执行某个通知,也就是执行某个方法事件
类名, 方法名和返回值可以自定义, 重要的是注解
接口返回为数据时, 需要加 @ResponseBody 注解
还可以针对不同的异常, 返回不同的结果
java
@ResponseBody
@ControllerAdvice
public class ErrorAdvice {
@ExceptionHandler
public Object handler(Exception e) {
return Result.fail(e.getMessage());
}
@ExceptionHandler
public Object handler(NullPointerException e) {
return Result.fail("发生NullPointerException: "+ e.getMessage());
}
@ExceptionHandler
public Object handler(ArithmeticException e) {
return Result.fail("发生ArithmeticException: "+ e.getMessage());
}
}
当有多个异常通知时,匹配顺序为当前类及其子类向上依次匹配
4. 总结
- 拦截器的实现主要分两部分: 1. 定义拦截器(实现HandlerInterceptor 接口) 2. 配置拦截器(实现WebMvcConfigurer接口)
- 统一数据返回格式通过@ControllerAdvice + ResponseBodyAdvice 来实现
- 统一异常处理使用@ControllerAdvice + @ExceptionHandler 来实现