全局 spring validation

该博客介绍了如何通过Spring AOP实现全局参数校验,以提高代码的优雅性和可维护性。作者引入了`spring-boot-starter-validation`依赖,并创建了一个切面类,在方法调用前对所有Controller请求进行参数校验。当检测到校验失败时,会抛出自定义异常。测试结果显示,这种方式简化了校验代码,但不支持分组校验。
摘要由CSDN通过智能技术生成

背景

由于项目中的很多地方都需要校验请求参数,但是一般都是2种方式

  1. 注解式 @Validate
    controller方法的参数之前加上注解,则会自动触发校验
  2. 编程式
    直接在业务代码里面写上校验方法 javax.validation.Validator.validate(arg)

上面的2种方法我都使用过,因为每做一个新功能都需要加上校验注解或者代码,总感觉不够优雅,所以就像做一个全局的方式,最后决定尝试使用 AOP

代码实现

1. 依赖
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-validation</artifactId>
        </dependency>
2. 代码
package com.gly.memo.common.aspect;

import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.gly.memo.common.exception.MyException;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import javax.validation.ConstraintViolation;
import javax.validation.Validator;
import java.util.HashSet;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * 参数校验切面
 *
 * @author gaojie
 * @date 2022-11-16
 */
@Slf4j
@Aspect
@Order(20)
@Component
public class ValidAspect {

    @Resource
    private Validator validator;

    /**
     * 使用前置通知,拦截所有的 controller 请求
     */
    @Before(value = "within(com.gly.memo.controller.*)")
    public void around(JoinPoint joinPoint) {

        // 1.获取参数校验结果
        Set<ConstraintViolation<Object>> validateSet = new HashSet<>();
        for (Object arg : joinPoint.getArgs()) {
            validateSet.addAll(validator.validate(arg));
        }

        // 2.若存在校验异常:则抛出业务异常
        if (CollectionUtils.isNotEmpty(validateSet)) {
            String message = validateSet.stream().map(ConstraintViolation::getMessageTemplate).collect(Collectors.joining(","));
            log.warn("参数校验失败:{}", validateSet);
            throw MyException.error(message);
        }
    }
}

测试

1.对象定义
@Data
public class LoginReq {

    @NotBlank(message = "账号-不能为空")
    @ApiModelProperty(value = "账号", required = true)
    private String username;

    @NotBlank(message = "密码-不能为空")
    @ApiModelProperty(value = "密码", required = true)
    private String password;
}
2.控制器
package com.gly.memo.controller;

import com.gly.memo.common.ResultVo;
import com.gly.memo.dto.req.LoginReq;
import com.gly.memo.facade.AuthenticationFacade;
import com.gly.memo.utils.ThreadLocalUtils;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;

/**
 * 认证管理:登录、退出
 *
 * @author gaojie
 * @date 2022-11-10
 */
@Api(tags = "认证管理")
@RestController
@RequestMapping("/memo")
public class AuthController {

    @Resource
    private AuthenticationFacade authenticationFacade;

    @ApiOperation("登录")
    @PostMapping("/login")
    public ResultVo login(@RequestBody LoginReq req) {
        return ResultVo.ok(authenticationFacade.login(req));
    }

    @ApiOperation("退出")
    @PostMapping("/logout")
    public ResultVo logout() {
        authenticationFacade.logout(ThreadLocalUtils.getToken());
        return ResultVo.ok();
    }

}

3.测试结果

控制台打印日志

接口返回信息

总结

优点:不需要异常特殊处理,简单明了,以后有新功能开发,也不用处理
缺点:不支持分组校验(个人感觉分组校验不怎么重要,可以舍弃或者其他方式实现)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值