作者:维生素-熙 | 来源:互联网 | 2023-08-14 16:43
前言在自己的摸索下对HibernateValidator有了初步的认识,可以使用已有的约束条件对字段做出限制,减少不要代码的出现,使代码更简洁。但在最近的实际使用中,出现了一些无法
前言
在自己的摸索下对HibernateValidator有了初步的认识,可以使用已有的约束条件对字段做出限制,减少不要代码的出现,使代码更简洁。
但在最近的实际使用中,出现了一些无法使用框架处理的问题,例如,在第三方请求我的接口时,根据status字段区分不同的业务逻辑;status=1进行A逻辑处理,status=2进行B逻辑处理;在网络了检索了相关信息后,做出如下总结。
步骤
使用通用Mapper插件生成实体类和mapper,这里为了聚焦对HibernateValidator的使用,把关注点放在实体类Brand中,在实体类中,对status字段添加自定义约束@StatusConstraint
package com.codeup.mybatisjoin.model;
import com.codeup.mybatisjoin.validation.StatusConstraint;
import lombok.Data;
import javax.persistence.Column;
import javax.persistence.Id;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
@Data
public class Brand {
@Id
@Column(name = "brand_ID")
private Long brandId;
/**
* 使用`@NotNull`添加约束
*/
@Column(name = "vendor_ID")
@NotNull(message = "[vendorId]不可为空")
private Long vendorId;
/**
* 使用`@NotBlank`添加约束
*/
@Column(name = "brand_name")
@NotBlank(message = "[brandName]字段不可为空")
private String brandName;
private String description;
/**
* 自定义约束
*/
@StatusConstraint(message = "[status]为1或者2")
private String status;
}
@StatusConstraint
实现步骤和自定义注解相似
package com.codeup.mybatisjoin.validation;
import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @ProjectName: mybatisjoin
* @Package: com.codeup.mybatisjoin.validation
* @ClassName: StatusConstraint
* @Author: lhc
* @Description: 状态约束
* @Date: 2019/8/22 下午 3:36
*/
@Target({ElementType.FIELD, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
/**
* 指出当前约束是通过`StatusConstraintValidator.class`来实现的
*/
@Constraint(validatedBy = StatusConstraintValidator.class)
public @interface StatusConstraint {
/**
* 配置message信息
* @return
*/
String message() default "违规参数";
/**
* 分组
* @return
*/
Class>[] groups() default {};
Class extends Payload>[] payload() default {};
}
通过查看源代码里面的文档得知要要实现ConstraintValidator
接口
@Documented
@Target({ ANNOTATION_TYPE })
@Retention(RUNTIME)
public @interface Constraint {
/**
* {@link ConstraintValidator} classes implementing the constraint. The given
classes
* must reference distinct target types for a given {@link ValidationTarget}.
If two
* {@code ConstraintValidator}s refer to the same type, an exception will
occur.
*
* At most one {@code ConstraintValidator} targeting the array of parameters of
* methods or constructors (aka cross-parameter) is accepted. If two or more
* are present, an exception will occur.
*
* @return array of {@code ConstraintValidator} classes implementing the
constraint
*/
Class extends ConstraintValidator, ?>>[] validatedBy();
}
实现ConstraintValidator
接口,重写isValid()
方法,实现自己的判断逻辑
package com.codeup.mybatisjoin.validation;
import lombok.extern.slf4j.Slf4j;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
/**
* @ProjectName: mybatisjoin
* @Package: com.codeup.mybatisjoin.validation
* @ClassName: StatusConstraintValidator
* @Author: lhc
* @Description: TODO
* @Date: 2019/8/22 下午 3:38
*/
@Slf4j
public class StatusConstraintValidator implements
ConstraintValidator {
@Override
public boolean isValid(Object value, ConstraintValidatorContext context) {
String statCode = (String) value;
// 等于1或2返回true,反之
if ("1".equals(statCode) || "2".equals(statCode)) {
return true;
}
return false;
}
}
至此,对字段添加约束条件已完成。还存在一个重要的操作是对约束的校验,这里通过工具类实现
package com.codeup.mybatisjoin.validation;
import org.hibernate.validator.HibernateValidator;
import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.Validator;
import java.util.Set;
/**
* @ProjectName: mybatisjoin
* @Package: com.codeup.mybatisjoin.validation
* @ClassName: ValidatorConfig
* @Author: lhc
* @Description: TODO
* @Date: 2019/8/22 下午 4:31
*/
public class ValidatorUtil {
/**
* 配置hibernate_validator和快速失败模式
*/
private static Validator validator =
Validation.byProvider(HibernateValidator.class)
.configure()
.failFast(true)
.buildValidatorFactory()
.getValidator();
/**
* 参数校验,若未匹配约束,则通过已将将之前定义的`message`抛出
* @param object 参数
* @param groups 属于组
*/
public static void result(Object object, Class>... groups) {
Set cOnstraintViolations=
validator.validate(object, groups);
if (constraintViolations.size() > 0) {
String message = constraintViolations.iterator().next().getMessage();
throw new MissingParameterException(message);
}
}
}
将message信息抛出之后,需要以更统一的方式返回给第三方,使用统一异常处理机制解决
自定义异常类MissingParameterException
package com.codeup.mybatisjoin.validation;
public class MissingParameterException extends RuntimeException {
public MissingParameterException(String message) {
super(message);
}
}
统一异常处理
package com.codeup.mybatisjoin.validation;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import java.util.HashMap;
import java.util.Map;
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(MissingParameterException.class)
public Map invalidParameter(MissingParameterException e) {
Map map = new HashMap<>();
map.put("code", 500);
map.put("message", e.getMessage());
return map;
}
}
通过请求查看返回结果
package com.codeup.mybatisjoin.controller;
import com.codeup.mybatisjoin.model.Brand;
import com.codeup.mybatisjoin.validation.ValidatorUtil;
import lombok.extern.slf4j.Slf4j;
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 java.util.LinkedHashMap;
import java.util.Map;
@Slf4j
@RestController
@RequestMapping(value = "/brandConroller")
public class BrandConroller {
@PostMapping(value = "/go")
public Map go(@RequestBody Brand brand) {
ValidatorUtil.result(brand);
Map map = new LinkedHashMap<>();
log.info("brand:{}", brand);
return map;
}
}
请求
// request
{
"brandId": 1,
"vendorId": 1,
"brandName": "demoData",
"description": "demoData",
"status": "3"
}
// response
{
"code": 500,
"message": "[status]为1或者2"
}
如有不妥之处,请吐槽我