Java使用@Validated注解进行参数验证的方法

网友投稿 243 2022-12-30


Java使用@Validated注解进行参数验证的方法

目前项目中大部分代码进行参数验证都是写代码进行验证,为了提升方便性和代码的简洁性,所以整理了下使用注解进行参数验证。使用效果如下:

// 要验证的实体类

@Data

public class User implements Serializable {

@NotBlank(message = "id不能为空!",groups = Update.class)

protected String id = "";

@NotBlank(message = "商户id不能为空!")

protected String tenantId;

@NotBlank(message = "名称不能为空!")

protected String name = "";

}

// controller

// 在要验证的参数上添加@Validated注解即可

@PostMapping("add")

public boolean addUser(@Validated @RequestBody User user) {

ProdAllotStandard info = allotStandardService.getProdAllotStandardWithDetailById(id);

return info;

}

// 修改则需要比添加的基础上多验证一个id,可以通过group的方式进行区分

// 实体类上不设置groups,默认会是Default,所以修改的时候,我们需要指定Update和Default这两个组

// group是可以自定义的,我默认定义了Add,Update,Delete,Search这四个组

@PostMapping("update")

public boolean updateUser(@Validated({Update.class, Default.class}) @RequestBody User user) {

ProdAllotStandard info = allotStandardService.getProdAllotStandardWithDetailById(id);

return info;

}

// 当然该注解也支持service上实现,同样的使用方法,在service的实现类的参数上加上对应注解即可,如:

public boolean addUser(@Validated User user) {

}

// 通过不同group的搭配,我们就可以灵活的在实体类上配置不同场景的验证了

// group为一个接口,用以下方式创建

public interface Add {

}

下面看一下具体的实现,验证框架的核心实现采用的是hibernate-validator,我采用的是5.4.3.Final版本。

controller和service的实现方式不同,下面分别介绍下。

不管哪种实现,我们都需要先定义一个异常和一个异常拦截器,当参数校验出现问题时,我们就抛出对应异常。

// 为了简短,省略了部分代码

public class ParamsException extends RuntimeException{

private String code;

public BusinessException() {

}

public ParamsException(String code,String message) {

super(message);

this.code = code;

}

public ParamsException(String message) {

super(message);

}

}

// 定义异常处理类

@ControllerAdvice

public class ExceptionAdvice {

private static Logger L = LoggerFactory.getLogger(ExceptionAdvice.class);

// 对所有的ParamsException统一进行拦截处理,如果捕获到该异常,则封装成MessageBody返回给前端

@ExceptionHandler(value = ParamsException.class)

@ResponseStatus(HttpStatus.OK)

@ResponseBody

public MessageBody handleParamsException(HttpServletRequest request, BusinessException e){

L.error(e.getMessage(),e);

return getErrorMessageBody(e.getData(),e.getMessage(),e.getMessage(),

StringUtils.isEmpty(e.getCode())?ResponseCode.BUSINESS_ERROR:e.getCode());

}

private MessageBody getErrorMessageBody(Object data,String message,String errorInfo,String code){

MessageBody body = new MessageBody();

body.setCode(code);

body.setData(data);

body.setErrorCode(Integer.parseInt(code));

body.setErrorInfo(errorInfo);

body.setMessage(message);

body.setSuccess(false);

return body;

}

}

controller的验证的实现:

主要是通过实现spring mvc给我们留下的接口进行实现的,该方案没有用到反射和代理。

1.  实现spring提供的SmartValidator接口

public class ParamsValidator implements SmartValidator {

private javax.validation.Validator validator = Validation.buildDefaultValidatorFactory().getValidator();

@Override

public boolean supports(Class> clazz) {

return true;

}

// 注解上没有有group的验证逻辑

@Override

public void validate(Object target, Errors errors) {

validate(target,errors,null);

}

// 注解上带有group的验证逻辑

// 第一个参数为我们要验证的参数,第二个不用管,第三个为注解上设置个groups

@Override

public void validate(Object target, Errors errors, Object... validationHints) {

// 这里面为验证实现,可以根据自己的需要进行完善与修改

if (target == null) {

throw new ParamsException("参数不能为空!");

} else {

if(target instanceof String) {

if(StringUtils.isEmpty(target)) {

throw new ParamsException("参数不能为空!");

}

}else if(target instanceof Collection) {

for(Object o:(Collection)target){

validate(o,validationHints);

}

}else {

validate(target,validationHints);

}

}

}

private void validate(Object target,Object ... objs) {

Set> violations;

// 没有groups的验证

if(objs==null || objs.length==0) {

violations = validator.validate(target);

} else {

// 基于groups的验证

Set> groups = new LinkedHashSet>();

for (Object hint : objs) {

if (hint instanceof Class) {

groups.add((Class>) hint);

}

}

violations = validator.validate(target, ClassUtils.toClassArray(groups));

}

// 若为空,则验证通过

if(violations==null||violations.isEmpty()) {

return;

}

// 验证不通过则抛出ParamsException异常。

for(ConstraintViolation item:violations) {

throw new ParamsException(item.getMessage());

}

}

}

2. 配置并设置ValiLsygZtLdator验证器

@Configuration

public class WebMvcConfigurer extends WebMvcConfigurerAdapter{

// 我们在这里重写spring的一个方法,返回我们自定义的验证器

@Override

public Validator getValidator() {

return createValidator();

}

@Bean

public ParamsValidator createValidator(){

return new ParamsValidator();

}

}

非常简单,通过上面配置,就可以在controller上使用注解了。

spring mvc对这个注解的处理主要是在RequestResponseBodyMethodProcessor这个类中的resolveArgument方法实现的,该类主要处理方法的参数和返回值。

spring mvc调用LsygZtL的一段代码。

protected void validateIfApplicabLsygZtLle(WebDataBinder binder, MethodParameter parameter) {

Annotation[] annotations = parameter.getParameterAnnotations();

for (Annotation ann : annotations) {

Validated validatedAnn = AnnotationUtils.getAnnotation(ann, Validated.class);

if (validatedAnn != null || ann.annotationType().getSimpleName().startsWith("Valid")) {

Object hints = (validatedAnn != null ? validatedAnn.value() : AnnotationUtils.getValue(ann));

Object[] validationHints = (hints instanceof Object[] ? (Object[]) hints : new Object[] {hints});

binder.validate(validationHints);

break;

}

}

}

service的验证的实现:

该方案主要通过aop进行拦截处理的。如下配置:

@Component

@Aspect

public class ParamsValidateAdvice {

// 这里重用上面定义的那个validator

private ParamsValidator validator = new ParamsValidator();

/**

* 拦截参数上加了@Validated的注解的方法

* 排除掉controller,因为controller有自己的参数校验实现 不需要aop

*/

@Pointcut("execution(* com.choice..*(..,@org.springframework.validation.annotation.Validated (*), ..)) && " +

"!execution(* com.choice..api..*(..)) && " +

"!execution(* com.choice..controller..*(..)) ")

public void pointCut(){}

@Before("pointCut()")

public void doBefore(JoinPoint joinPoint){

Object[] params=joinPoint.getArgs();

MethodSignature signature = (MethodSignature) joinPoint.getSignature();

Method method = signature.getMethod();

Parameter[] parameters = method.getParameters();

// 验证参数上的注解

for(int i=0;i

Parameter p = parameters[i];

// 获取参数上的注解

Validated validated = p.getAnnotation(Validated.class);

if(validated==null) {

continue;

LsygZtL }

// 如果设置了group

if(validated.value()!=null && validated.value().length>0) {

validator.validate(params[i],null,validated.value());

} else {

validator.validate(params[i],null);

}

}

}

}

这样就可以在service使用验证注解了,具体的Pointcut可以自己进行配置。

个人觉得参数校验在controller或者对外暴露的服务中去做就好了,因为这些都是对外提供服务的,controller层也应该去做这些,所以参数需要校验好。

没必要在自己内部调用的service中加校验。

Parameter p = parameters[i];

// 获取参数上的注解

Validated validated = p.getAnnotation(Validated.class);

if(validated==null) {

continue;

LsygZtL }

// 如果设置了group

if(validated.value()!=null && validated.value().length>0) {

validator.validate(params[i],null,validated.value());

} else {

validator.validate(params[i],null);

}

}

}

}

这样就可以在service使用验证注解了,具体的Pointcut可以自己进行配置。

个人觉得参数校验在controller或者对外暴露的服务中去做就好了,因为这些都是对外提供服务的,controller层也应该去做这些,所以参数需要校验好。

没必要在自己内部调用的service中加校验。


版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:开源api网关设计(开源api网关设计参数)
下一篇:网络直报系统接口设计方案(系统对外接口设计)
相关文章

 发表评论

暂时没有评论,来抢沙发吧~