Spring Boot 实战(一):拦截器 + 统一数据返回 + 统一异常处理,一站式搞定接口通用逻辑

目录

一、拦截器:统一处理通用业务逻辑

在大家刚刚学习spring过程中,尤其是写些带有用户强制登录的案例中,肯定遇到不少麻烦,包括于但不仅限于:需要修改每个接⼝的处理逻辑,需要修改每个接⼝的返回结果 ,接⼝定义修改,前端代码也需要跟着修改,苦不堪言,代码冗余就算了,开发的效率也大大折扣,这就需要引入拦截器了

什么是拦截器

拦截器是 SpringMVC 的核心组件,能在请求生命周期的关键节点执行自定义逻辑,常用于登录校验、接口访问日志、权限控制、参数校验等场景

也就是说,允许开发⼈员提前预定义⼀些逻辑,在⽤⼾的请求响应前后执⾏.也可以在⽤⼾请求前阻⽌其执⾏.

在拦截器当中,开发⼈员可以在应⽤程序中做⼀些通⽤性的操作,⽐如通过拦截器来拦截前端发来的请求,判断Session中是否有登录⽤⼾的信息.如果有就可以放⾏,如果没有就进⾏拦截

拦截器的基本使⽤

拦截器的使⽤步骤分为两步:

  1. 定义拦截器
  2. 注册配置拦截器

定义拦截器

实现HandlerInterceptor接⼝,并重写其所有⽅法

java 复制代码
@Slf4j
@Component
public class LoginInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        log.info("preHandle目标方法执行前");
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
       log.info("postHandle");

    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
       log.info("afterCompletion");

    }
 }
  • preHandle():在目标 Controller 方法执行之前被调用,如果是true就" 放行 " (类似于执行controller方法),如果是false 就"拦死"(不允许进去controller方法)

图中代码就通过session验证用户是否已经登录,如果没有登录就返回false,无法进如controller方法中

  • postHandle()⽅法:⽬标⽅法执⾏后执⾏(于preHandle()相反,执行完controller后拦截)
  • afterCompletion()⽅法:视图渲染完毕后执⾏,最后执⾏(后端开发现在⼏乎不涉及视图,暂不了解)

注册配置拦截器

实现WebMvcConfigurer接⼝,并重写addInterceptors⽅法

java 复制代码
@Configuration
public class WebConfig  implements WebMvcConfigurer {
 	//⾃定义的拦截器对象
    @Autowired
    LoginInterceptor loginInterceptor;
     //注册⾃定义拦截器对象
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(loginInterceptor)
        				.addPathPatterns("/**");
        //设置拦截器拦截的请求路径( /**表⽰拦截所有请求)

    }
}

执行查询图书id的请求观察后端⽇志

可以看到preHandle⽅法执⾏之后就放⾏了,开始执⾏⽬标⽅法,⽬标⽅法执⾏完成之后执⾏postHandle和afterCompletion⽅法.

拦截器详解

拦截器的拦截路径配置

addPathPatterns()

在上述代码中,我们通过addPathPatterns() 配置 /**路径表示拦截所有的请求,所以addPathPatterns() 表示⽅法指定要拦截哪些请求

excludePathPatterns()

有指定拦截哪些请求,也有excludePathPatterns() 指定不拦截哪些请求

拦截路径设置说明

拦截路径 含义 举例
/* ⼀级路径 能匹配/user,/book,/login,不能匹配/user/login
/** 任意级路径 能匹配/user,/user/login,/user/reg
/book/* /book下的⼀级路径 能匹配/book/addBook,不能匹配/book/addBook/1,/book
/book/** /book下的任意级路径 能匹配/book,/book/addBook,/book/addBook/2,不能匹配/user/login

拦截器执⾏流程

正常的调⽤顺序:

有了拦截器之后,会在调⽤Controller之前进⾏相应的业务处理,执⾏的流程如下图:

  1. 添加拦截器后,执⾏Controller的⽅法之前,请求会先被拦截器拦截住.执⾏
    preHandle() ⽅法,这个⽅法需要返回⼀个布尔类型的值.如果返回true,就表⽰放⾏本次操作,继续访问controller中的⽅法.如果返回false,则不会放⾏(controller中的⽅法也不会执⾏)
  2. controller当中的⽅法执⾏完毕后,再回过来执⾏postHandle() 这个⽅法以及afterCompletion() ⽅法,执⾏完毕之后,最终给浏览器响应数据

二、统一数据返回格式:告别杂乱的返回值

刚刚通过学会了利用拦截器来统一处理通用业务逻辑,问题又来了,比如在我们使用前端对接时要适配多种格式,极易出 bug;后端排查问题时也无法快速定位(比如不知道是 "参数错" 还是 "服务器错"),同样的苦不堪言,这就需要用到统⼀数据返回格式了,让我们告别杂乱的返回值

什么是统一数据返回格式

统一数据返回格式,就是让 Spring Boot 项目中所有接口 (无论成功 / 失败、有数据 / 无数据)返回的 JSON 结构完全一致,字段固定、含义明确

统一数据返回格式的基本使⽤

统⼀的数据返回格式使⽤@ControllerAdviceResponseBodyAdvice 的⽅式实现

@ControllerAdvice 表⽰控制器通知类添加类 ResponseAdvice ,实现@ControllerAdvice 注解

java 复制代码
@ControllerAdvice
public class ResponseAdvice  implements ResponseBodyAdvice {
    @Autowired
    ObjectMapper objectMapper;
    @Override
    public boolean supports(MethodParameter returnType, Class converterType) {
        //false 不处理 true 处理
        return true;
    }
    @Override
    public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
        return  Result.success(body);
    }
}
  • supports⽅法 : 判断是否要执⾏ beforeBodyWrite⽅法.true 为执⾏,false 不执⾏. 通过该⽅法可以选择哪些类或哪些⽅法的response要进⾏处理,其他的不进⾏处理
  • beforeBodyWrite⽅法 : 对response⽅法进⾏具体操作处理

测试查询图书id的请求

添加统⼀数据返回格式之前:

添加统⼀数据返回格式之后:

使用String类型存在问题

接口代码:

java 复制代码
@RequestMapping(value = "/deleteBook",produces = "application/json")
    public String deleteBook(Integer bookId){
        log.info("删除图书,bookid: {}",bookId);
        try {
            BookInfo bookInfo=new BookInfo();
            bookInfo.setId(bookId);
            bookInfo.setStatus(BookStatusEnum.DELETED.getCode());
            bookService.updateBook(bookInfo);
            //成功
            return  "";
        } catch (Exception e){
            log.error("修改图书发生异常,e",e);
            return  "修改图书发生异常";
        }
    }

当我们执行增加图书的接口

测试结果:

查看一下日志

这个日志显示的是类型转换异常(ClassCastException)

多测试几种返回类型,发现只要返回结果为String类型时才有这种错误发⽣,原因是接口返回了String类型的数据 ,全局响应包装(如GlobalResponseAdvice)将其自动包装为Result对象 ,但 Spring 的StringHttpMessageConverter会优先处理 String 类型的返回值,试图将包装后的Result强制转换为String,从而触发类型转换异常

解决办法

则将其包装为统一的 Result 格式对象 ,再通过 ObjectMapper 序列化为标准 JSON 字符串返回

java 复制代码
    @Override
    public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
//        如果为string得进行封装 对于string类型,前端接收不到,所有全部给改成json类似,加producee="application/json
        if (body instanceof String) {
            return objectMapper.writeValueAsString(Result.success(body));
        }
        return  Result.success(body);
    }
}

测试接口结果:

三、统一异常处理

什么是统一异常处理

统一异常处理实现的全局异常捕获机制 ,能集中捕获项目中所有接口抛出的异常(业务异常、参数异常、系统异常等),并将异常信息封装成和成功响应一致的统一格式(如之前的 Result 类)返回给前端,替代零散的 try-catch 处理

统一异常处理的基本使用

统⼀异常处理使⽤的是@ControllerAdvice +@ExceptionHandler

来实现的, @ControllerAdvice 表⽰控制器通知类,@ExceptionHandler 是异常处理器,两个结合表⽰当出现异常的时候执⾏某个通知,也就是执⾏某个⽅法事件

java 复制代码
@Slf4j
@ControllerAdvice
@ResponseBody
public class ExceptionAdvice {
    @ExceptionHandler
    public Result handler(Exception e) {
                log.error("发生异常,e",e);
        return Result.fail("内容异常,请联系管理员");

    }

以上代码表⽰,如果代码出现Exception异常(包括Exception的⼦类),就返回⼀个Result的对象.对象的设置参考Result.fail(e.getMessage())

java 复制代码
public static Result fail(String msg) {
 Result result = new Result();
 result.setStatus(ResultStatus.FAIL);
 result.setErrorMessage(msg);
 result.setData("");
 return result;
 }

这边模拟制造异常:当添加统⼀数据返回格式使用String类型存在问题的返回:

其他说明:

类名,⽅法名和返回值可以⾃定义,重要的是注解

@RestControllerAdvice=@ControllerAdvice +@ResponseBody

当有多个异常通知时,匹配顺序为当前类及其⼦类向上依次匹配

相关推荐
渣渣盟1 小时前
Console登录安全配置指南
运维·服务器·网络
掘根1 小时前
【消息队列项目】Muduo库的介绍
运维·服务器
喵个咪1 小时前
C++ 类型转换:旧风格与四种新风格详解
c++·后端
廋到被风吹走1 小时前
【JDK版本】JDK1.8相比JDK1.7 语言特性之函数式编程
java·开发语言·python
fire-flyer1 小时前
Reactor Context 详解
java·开发语言
3***89191 小时前
TypeScript 与后端开发Node.js
java
老兵发新帖1 小时前
Spring Boot 的配置文件加载优先级和合并机制分析
java·spring boot·后端
明洞日记1 小时前
【JavaWeb手册004】Spring Boot的核心理念
java·spring boot·后端
gs801401 小时前
华鲲振宇 AT3500 G3 深度解析 —— 面向大模型推理的国产异构算力服务器
运维·服务器