背景
由于项目中的很多地方都需要校验请求参数,但是一般都是2种方式
- 注解式
@Validate
在controller
方法的参数之前加上注解,则会自动触发校验- 编程式
直接在业务代码里面写上校验方法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();
}
}