统一功能处理

目录

一、拦截器

自定义拦截器:

注册配置拦截器:

拦截器执行流程

二、统一数据返回格式

三、统一异常处理


一、拦截器

拦截器是spring框架提供的核心功能之一,它可以拦截用户请求,在指定方法前后执行预先设定的代码。

比如我们可以通过拦截器拦截前端发来的请求,判断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("LoginInterceptor ⽬标⽅法执⾏前执⾏..");
        return true;
    }
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse
response, Object handler, ModelAndView modelAndView) throws Exception {
        log.info("LoginInterceptor ⽬标⽅法执⾏后执⾏");
    }
    @Override
    public void afterCompletion(HttpServletRequest request,
HttpServletResponse response, Object handler, Exception ex) throws Exception {
        log.info("LoginInterceptor 视图渲染完毕后执⾏,最后执⾏");
    }
}

我们一般主要关注preHandle方法,它是在目标方法执行前执行,这个方法返回true就放行,返回false就进行拦截。

我们可以在preHandle方法中进行用户登录的判断,用户没有登录就进行拦截,不执行目标方法,

比如:

java 复制代码
 @Override
public boolean  preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        //System.out.println("目标方法执行前......");
        log.info("登录拦截器进行拦截校验...");
        //判断用户是否登录
        HttpSession session = request.getSession(false);//false表示request中没有session不会创建一个session
        if(session == null){
            response.setStatus(401);//http状态码,不是业务码
            response.setContentType("text/html;charset=utf-8");//设置编码
            response.getOutputStream().write("用户未登录".getBytes(StandardCharsets.UTF_8));

            return false;
        }
        //true 放行,false 拦截
        return true;
    }

注册配置拦截器:

实现WebMvcConfigurer 接口,并重写addInterceptors方法

java 复制代码
@Configuration
public class WebConfig implements WebMvcConfigurer {
    //⾃定义的拦截器对象
    @Autowired
    private LoginInterceptor loginInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        //注册⾃定义拦截器对象
        registry.addInterceptor(loginInterceptor)
        .addPathPatterns("/**");//设置拦截器拦截的请求路径( /** 表⽰拦截所
有请求)
        .excludePathPatterns("/user/login")//表示排除这个路径,允许放行
        .excludePathPatterns("/**/*.js") //排除前端静态资源
        .excludePathPatterns("/**/*.css")
        .excludePathPatterns("/**/*.png")
        .excludePathPatterns("/**/*.html");
    }
}

|------------|-------------|-------------------------------------------|
| 路径 | 含义 | 举例 |
| /* | 一级路径 | 能匹配/book, /user, 不能匹配/book/list |
| /** | 任意级路径 | 匹配/book, /book/list, /user/login, /user |
| /book/* | book下的一级路径 | 匹配/book/add, /book/list, 不能匹配/book/add/1 |
| /book/** | book下的任意级路径 | 匹配/book/add, /book/add/1, 不能匹配/user/login |
[拦截器路径设置]

拦截

拦截器执行流程

用户发送请求,在执行controller中的目标方法之前先调用拦截器中的preHandle方法,

preHandle方法返回true就放行 执行目标方法,preHandle方法返回false就拦截 不执行目标方法。

然后执行拦截器中的postHandle、afterCompletion方法。

二、统一数据返回格式

java 复制代码
import com.example.demo.model.Result;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;

@ControllerAdvice
public class ResponseAdvice implements ResponseBodyAdvice {
    @Override
    public boolean supports(MethodParameter returnType, Class converterType) {
        return true;
    }
    @Override
    public Object beforeBodyWrite(Object body, MethodParameter returnType,
MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest
request, ServerHttpResponse response) {
        return xxx;//根据需求统一返回数据
    }
}

@ControllerAdvice是控制器通知类,有这个注解的类对象也是交由spring loc容器进行管理,它是派生于@Component注解的

通知类需要实现ResponseBodyAdvice接口,重写它的supports和beforeBodyWrite方法,并添加@ControllerAdvice注解。

supports方法返回true表示执行beforeBodyWrite方法,返回false表示不执行。

响应正文写入之前执行beforeBodyWrite方法,可以在这做统一处理,统一返回数据格式。beforeBodyWrite方法的参数body是指目标方法的返回值,returnType是目标方法的返回类型。

因此可以通过supports方法来确定哪些方法的返回值需要在beforeBodyWrite中进行统一处理,哪些不需要处理。

比如:

java 复制代码
    @Override
    public boolean supports(MethodParameter returnType, Class converterType) {
        //是否要执行下面的方法(beforeBodyWrite),可以做不同处理
        //比如:
        if(returnType.getDeclaringClass().equals(UserController.class)){
            return false;
        }
        //false:不执行下面的方法
        //true:执行下面的方法
        return true;
    }

这个代码就表示如果目标方法的返回值类型是UserController类就不执行beforeBodyWrite方法,其他的返回值类型都执行beforeBodyWrite方法。

java 复制代码
 @Override
    public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
        if(body instanceof Result<?>){
            return body;
        }
        //返回数据body类型是String需要处理
        if(body instanceof String){
            try {
                return objectMapper.writeValueAsString(Result.success(body));
            } catch (JsonProcessingException e) {
                throw new RuntimeException(e);
            }
        }
        return Result.success(body);
    }

这个重写的beforeBodyWrite方法中统一返回的类型都是Result,第一个if语句表示如果目标方法返回值类型就是Result,就直接return。

后面的就是对返回值进行统一处理:

当控制器方法返回String类型时 ,Spring 默认会将其视为视图名称或直接文本响应,而不是 JSON 格式。如果不手动转换为 JSON,客户端可能会收到纯文本而非结构化的 JSON 数据。

(简言之,beforeBodyWrite方法的参数body如果是String类型,那么必须手动将要return的内容转换成json格式)

下面这个代码就是我们定义的统一数据返回的格式:

java 复制代码
package com.bit.book.model;

import com.bit.book.enums.CodeStatus;
import lombok.Data;

@Data
public class Result<T> {
    //业务状态码
    //自己定义
    //200正常  -1 未登录  -2 异常 ...
    private CodeStatus code;

    private String errorMsg;
    private T data;

    public static <T> Result<T> success(T data){
        Result result = new Result();
        result.setCode(CodeStatus.SUCCESS);
        result.setData(data);
        return result;
    }
    public static <T> Result<T> fail(String errMsg){
        Result result = new Result();
        result.setCode(CodeStatus.ERROR);
        result.setErrorMsg(errMsg);
        return result;
    }

    public static Result unLogin(){
        Result result = new Result();
        result.setCode(CodeStatus.UNLOGIN);
        result.setErrorMsg("用户未登录");
        return result;
    }

    public static <T> Result<T> unLogin(T data){
        Result result = new Result();
        result.setCode(CodeStatus.UNLOGIN);
        result.setErrorMsg("用户未登录");
        result.setData(data);
        return result;
    }
}

统一数据返回格式有利于前端更好地接收、分析数据,有利于项目统一数据的维护、修改。

三、统一异常处理

统一异常处理是使用@ControllerAdvice和@ExceptionHandler来实现的。

@ControllerAdvice表示控制器通知类,@ExceptionHandler是异常处理器,

这两个注解结合就表示当出现异常的时候执行某个方法。

类名、方法名、返回值可以自定义。

比如:

java 复制代码
import com.bit.book.model.Result;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;

@Slf4j
@ControllerAdvice
@ResponseBody//这个注释一定要加,表示返回的是数据,不加返回的就是页面 会出错
public class ResponseExceptionHandle {
    @ExceptionHandler
    public Result exceptionHandler(Exception e){
        log.error("发生异常,e:",e);
        return Result.fail("内部错误,e: " + e);
    }
    @ExceptionHandler
    public Result exceptionHandler(NullPointerException e){
        log.error("空指针异常, e:",e);
        return Result.fail("空指针异常,e:"+ e);
    }
    @ExceptionHandler
    public Result exceptionHandler(ArithmeticException e){
        log.error("算术运算异常,e:",e);
        return Result.fail("算术运算异常,e:"+ e);
    }
    @ExceptionHandler
    public Result exceptionHandler(IndexOutOfBoundsException e){
        log.error("数组越界异常,e:",e);
        return Result.fail("数组越界异常,e:"+ e);
    }
}

还有另一种写法:

java 复制代码
package com.bit.book.advice;

import com.bit.book.model.Result;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;

@Slf4j
@ControllerAdvice
@ResponseBody//这个注释一定要加,表示返回的是数据,不加返回的就是页面 会出错
public class ResponseExceptionHandle {
    //第二种
    @ExceptionHandler(Exception.class)
    public Result exceptionHandler1(Exception e){
        log.error("发生异常,e:",e);
        return Result.fail("内部错误,e: " + e);
    }
    @ExceptionHandler(NullPointerException.class)
    public Result exceptionHandler2(Exception e){
        log.error("空指针异常, e:",e);
        return Result.fail("空指针异常,e:"+ e);
    }
    @ExceptionHandler(ArithmeticException.class)
    public Result exceptionHandler3(Exception e){
        log.error("算术运算异常,e:",e);
        return Result.fail("算术运算异常,e:"+ e);
    }
    @ExceptionHandler(IndexOutOfBoundsException.class)
    public Result exceptionHandler4(Exception e){
        log.error("数组越界异常,e:",e);
        return Result.fail("数组越界异常,e:"+ e);
    }
}

当出现对应的异常时,前端就会收到我们处理的异常数据,这里的Result.fail()就是前面Result类中的静态方法

复制代码
public static <T> Result<T> fail(String errMsg){
    Result result = new Result();
    result.setCode(CodeStatus.ERROR);
    result.setErrorMsg(errMsg);
    return result;
}

比如我们写一个TestController,来测试一下算术运算异常:

java 复制代码
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/test")
public class TestController {
    @RequestMapping("/t1")
    public int t1(){
        return 10/0;
    }
}

运行程序,在浏览器上访问接口(如果配置了拦截器,记得放行这个路径):

复制代码
{
  "code": "ERROR",
  "errorMsg": "算术运算异常,e:java.lang.ArithmeticException: / by zero",
  "data": null
}
相关推荐
摇滚侠15 分钟前
DBeaver 导入数据库 导入 SQL 文件 MySQL 备份恢复
java·数据库·mysql
keep one's resolveY39 分钟前
SpringBoot实现重试机制的四种方案
java·spring boot·后端
天空属于哈夫克31 小时前
企业微信API常见的错误和解决方案
java·数据库·企业微信
摇滚侠2 小时前
VMvare 虚拟机 Oracle19c 安装步骤,远程连接 Oracle19c,百度网盘安装包
java·oracle
梁萌2 小时前
idea报错找不到XX包的解决方法
java·intellij-idea·启动报错·缺少包
Agent产品评测局2 小时前
生产排期与MES/ERP系统打通,实操方法详解 —— 2026企业级智能体自动化选型与实战指南
java·运维·人工智能·ai·chatgpt·自动化
阿丰资源2 小时前
基于Spring Boot的电影城管理系统(直接运行)
java·spring boot·后端
呱牛do it3 小时前
企业级门户网站设计与实现:基于SpringBoot + Vue3的全栈解决方案(Day 8)
java
消失的旧时光-19434 小时前
Spring Boot 工程化进阶:统一返回 + 全局异常 + AOP 通用工具包
java·spring boot·后端·aop·自定义注解
NE_STOP4 小时前
Redis--发布订阅命令和Redis事务
java