java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > Spring  Validation输入验证

利用Spring Validation实现输入验证功能

作者:半亩方塘立身

这篇文章主要给大家介绍了如何利用Spring Validation完美的实现输入验证功能,文中有详细的代码示例,具有一定的参考价值,感兴趣的朋友可以借鉴一下

概述

校验例子

大家平时编码中经常涉及参数的校验,对于一个用户注册的方法来说会校验用户名密码信息:

public class UserController {
    public ResponseEntity<String> registerUser(String username, String password) {
        if (username == null || username.isEmpty()) {
            return ResponseEntity.badRequest().body("用户名不能为空");
        }
        if (password == null || password.isEmpty()) {
            return ResponseEntity.badRequest().body("密码不能为空");
        }
        if (password.length() < 6) {
            return ResponseEntity.badRequest().body("密码长度至少为6位");
        }
        // 处理用户注册逻辑
        return ResponseEntity.ok("用户注册成功");
    }
}

上述例子中需要手动编写参数校验逻辑的过程。虽然对于这个简单的示例而言,手动编写校验逻辑可能是可行的,但是对于复杂的验证规则和多个参数的情况,手动编写校验逻辑会变得冗长、难以维护和复用。

引入现代的校验框架如Spring Validation可以帮助解决这些问题,提供更高效、统一和可维护的参数校验方案。

Bean Validation规范

Bean Validation的主页:Jakarta Bean Validation - Home

Bean Validation的参考实现:GitHub - hibernate/hibernate-validator: Hibernate Validator - Jakarta Bean Validation Reference Implementation

相关版本兼容性

Bean ValidationHibernate ValidationJDKSpring Boot
1.15.4 +6+1.5.x
2.06.0 +8+2.0.x
3.07.0 +9+2.0.x

3.0后Bean Validation改名为Jakarta Bean Validation 3.0了。 如果你的项目版本是jdk1.8的,不要使用hibernate-validator 7.0的版本,它里面的依赖的jakarta.validation-api:3.0是需要jdk1.9的部分支持的。

Spring Validation注解

Spring Validation建立在Java Bean Validation(JSR 380)的基础上,为开发人员提供了一组注解和工具,用于定义和执行数据验证规则。它允许开发人员在应用程序中定义验证规则,并使用这些规则来验证输入数据、请求参数、领域对象等。

@Validated:可以用在类型、方法和方法参数上。但是不能用在成员属性(字段)上

@Valid:可以用在方法、构造函数、方法参数和成员属性(字段)上

常用注解标签如下:

标签说明
@Null限制只能为null
@NotNull限制必须不为null
@AssertFalse限制必须为false
@AssertTrue限制必须为true
@DecimalMax(value)限制必须为一个不大于指定值的数字
@DecimalMin(value)限制必须为一个不小于指定值的数字
@Digits(integer,fraction)限制必须为一个小数,且整数部分的位数不能超过integer,小数部分的位数不能超过fraction
@Future限制必须是一个将来的日期
@Max(value)限制必须为一个不大于指定值的数字
@Min(value)限制必须为一个不小于指定值的数字
@Past限制必须是一个过去的日期
@Pattern(value)限制必须符合指定的正则表达式
@Size(max,min)限制字符长度必须在min到max之间
@Past验证注解的元素值(日期类型)比当前时间早
@NotEmpty验证注解的元素值不为null且不为空(字符串长度不为0、集合大小不为0)
@NotBlank验证注解的元素值不为空(不为null、去除首位空格后长度为0),不同于@NotEmpty,@NotBlank只应用于字符串且在比较时会去除字符串的空格
@Email验证注解的元素值是Email,也可以通过正则表达式和flag指定自定义的email格式

hibernate-validator 校验Java Bean

<dependency>
    <groupId>org.hibernate.validator</groupId>
    <artifactId>hibernate-validator</artifactId>
    <version>6.2.0.Final</version>
</dependency>
public class User {
    @NotBlank(message = "用户名不能为空")
    private String username;
    @Min(value = 18, message = "年龄不能小于18岁")
    private int age;
    // 构造函数、Getter 和 Setter 方法
}
public class ValidatorTest {
    public static void main(String[] args) {
        // 创建校验器
        ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
        Validator validator = factory.getValidator();
        // 创建用户对象
        User user = new User();
        user.setUsername("");
        user.setAge(16);
        // 执行校验
        Set<ConstraintViolation<User>> violations = validator.validate(user);
        // 处理校验结果
        if (!violations.isEmpty()) {
            for (ConstraintViolation<User> violation : violations) {
                System.out.println(violation.getMessage());
            }
        } else {
            System.out.println("校验通过");
        }
    }
}

用Spring Validation提高生产力

Spring Validation引入

添加pom依赖

Spring Validation校验包被独立成了一个starter组件,引入如下依赖:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-validation</artifactId>
</dependency>

如果是spring-boot-starter-web不用引入了,spring-boot-starter-web 集成了spring-boot-starter-validation,默认可以不加spring-boot-starter-validation,它同时也集成了hibernate-validator

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

hibernate Validator校验器

这里我们定义hibernate校验器用于校验参数

@Configuration
@EnableAutoConfiguration
public class HibernateValidatorConfiguration {
	@Bean
	public MethodValidationPostProcessor methodValidationPostProcessor() {
		MethodValidationPostProcessor processor = new MethodValidationPostProcessor();
		processor.setValidator(validator());
		processor.setProxyTargetClass(true);
		return processor;
	}
	@Bean
	public Validator validator() {
		return Validation
				.byProvider(HibernateValidator.class)
				.configure()
				.addProperty("hibernate.validator.fail_fast", "true")
				.buildValidatorFactory()
				.getValidator();
	}
}

全局异常处理

每个Controller方法中都如果都写一遍对校验结果信息的处理,使用起来还是很繁琐。可以通过全局异常处理的方式统一处理校验异常。

@ControllerAdvice
@Slf4j
public class GlobalExceptionHandler {
    @ExceptionHandler(value = ConstraintViolationException.class)
	@ResponseBody
	public ApiResult defaultInsuranceExceptionHandler(ConstraintViolationException e) {
		log.error("校验错误:{}", e.getMessage());
		return ApiResult.error("error", e.getMessage());
	}
	@ExceptionHandler(value = MissingServletRequestParameterException.class)
	@ResponseBody
	public ApiResult handleMissingServletRequestParameter(MissingServletRequestParameterException ex) {
		String error = ex.getParameterName() + " 参数为空";
		return ApiResult.error("error", error);
	}
}

Controller接口Bean校验

首先,假设我们有一个用户注册的请求对象 UserRegistrationRequest,其中包含用户名和密码字段。

@Data
public class UserRegistrationRequest {
    @NotBlank(message = "用户名不能为空")
    private String username;
    @NotBlank(message = "密码不能为空")
    @Size(min = 6, message = "密码长度至少为6位")
    private String password;
}

我们使用了两个注解进行参数校验:

接下来我们定义一个Controller接口

@RestController
public class UserController {
    @PostMapping("/register")
    public ResponseEntity<String> registerUser(@Valid @RequestBody UserRegistrationRequest request) {
        // 处理用户注册逻辑
        return ResponseEntity.ok("用户注册成功");
    }
}

@RequestParam 参数校验

首先需要将MethodValidationPostProcessor设置成cglib代理

processor.setProxyTargetClass(true);

controller实现

@RestController
public class ValidController implements ValidClient {
	@Override
	public String queryById(Integer id) {
		return "id:" + id;
	}
}

分组校验

还是看上面那个Controller接口校验的例子,如果我们注册的时候需要校验用户名和密码,重置密码的时候只校验密码该怎么校验呢?这个时候就用到了分组校验了。

public interface UpdateGroup {
}

public class UserRegistrationRequest {
    @NotBlank(message = "用户名不能为空", groups = {RegistrationGroup.class})
    private String username;
    @NotBlank(message = "密码不能为空")
    @Size(min = 6, message = "密码长度至少为6位")
    private String password;
    // 其他字段和方法
}

在上述示例中,我们将@Validated注解应用到username字段,并通过groups属性指定了RegistrationGroup.class,这意味着在注册场景中会进行校验。

@RestController
public class UserController {
    @PostMapping("/register")
    public ResponseEntity<String> registerUser(@Validated(RegistrationGroup.class) @RequestBody UserRegistrationRequest request) {
        // 处理用户注册逻辑
        return ResponseEntity.ok("用户注册成功");
    }
    @PostMapping("/update")
    public ResponseEntity<String> updateUser(@Validated(UpdateGroup.class) @RequestBody UserRegistrationRequest request) {
        // 处理用户更新逻辑
        return ResponseEntity.ok("用户更新成功");
    }
}

在上述示例中,我们在updateUser方法中使用@Validated(UpdateGroup.class)来指定在更新场景中只执行UpdateGroup分组的验证规则。由于username字段上没有指定groups属性,所以在更新场景中将不会对username字段进行校验。

自定义注解

Spring 的 validation 为我们提供了许多特性,几乎可以满足日常开发中绝大多数参数校验场景了。但是,一个好的框架一定是方便扩展的。有了扩展能力,就能应对更多复杂的业务场景,下面我们自定义一个日期格式校验的注解

定义注解接口

@Documented
@Constraint(validatedBy = DateFormatValidator.class)
@Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.PARAMETER, ElementType.TYPE_USE})
@Retention(RetentionPolicy.RUNTIME)
public @interface DateFormat {
	//默认错误消息
	String message() default "时间格式错误";
	//分组
	Class<?>[] groups() default {};
	//默认日期格式
	String formatter() default "yyyy-MM-dd";
	//负载
	Class<? extends Payload>[] payload() default {};
	@Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.PARAMETER, ElementType.TYPE_USE})
	@Retention(RetentionPolicy.RUNTIME)
	@Documented
	@interface List {
		DateFormat[] value();
	}
}

定义校验器

public class DateFormatValidator implements ConstraintValidator<DateFormat, String> {
	protected String dateFormatter;
	@Override
	public void initialize(DateFormat constraintAnnotation) {
		this.dateFormatter = constraintAnnotation.formatter();
	}
	@Override
	public boolean isValid(String value, ConstraintValidatorContext constraintValidatorContext) {
		if (StringUtils.isNotBlank(value)) {
			try {
				DateUtils.parseDate(value, dateFormatter);
			} catch (Exception e) {
				return false;
			}
			return true;
		}
		return false;
	}
}

自定义校验注解使用起来和官方注解没有区别,在需要的字段上添加相应注解即可。

public class RequestParam {
    @DateFormat(message = "日期输入错误")
    private String beginDate;
    // 其他字段和方法
}

以上就是利用Spring Validation实现输入验证功能的详细内容,更多关于Spring Validation输入验证的资料请关注脚本之家其它相关文章!

您可能感兴趣的文章:
阅读全文