SpringBoot によるパラメータ検証の実行方法
日常のインターフェイス開発では、不正なパラメータがビジネスに影響を与えることを防ぐために、インターフェイスのパラメータを検証する必要があることがよくあります。パスワードが空かどうかを確認する ユーザーを作成するときは、電子メールと携帯電話番号の形式が正しいかどうかを確認する必要があります。コードに依存してインターフェイス パラメーターを 1 つずつ検証するのは非常に煩雑であり、コードの可読性は非常に低くなります。
Validator フレームワークは、開発者が開発中に記述するコードを減らし、開発効率を向上できるという問題を解決するように設計されています。Validator は、一般的に必要な検証、電子メール形式などのインターフェイス パラメーターを検証するために特に使用されます。検証、ユーザー名は 6 から 12 の間でなければなりません、など...
1. Validator 検証フレームワークの統合 1.1. 依存関係パッケージの導入バリデーター検証フレームワークは JSR-303 検証仕様 (パラメーター検証仕様) に従います。JSR は
Java 仕様の略称です。リクエスト###。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
注:springboot-2.3
から開始、検証パッケージは
starterコンポーネントに独立しているため、検証と Web を導入する必要がありますが、
springboot-2.3より前のバージョンでは Web 依存関係を導入するだけで済みます。
Function | ##@AssertFalse |
---|---|
#@AssertTrue | |
# にする必要があります。 | ##@DecimalMax |
@DecimalMin | |
@Digits | |
@Future | |
@Past | |
#@Max | |
@Min | |
#@NotNull | null は使用できません。空にすることもできます |
@Null | null である必要があります |
##@パターン | 指定された正規表現を満たす必要があります |
@Size | コレクション、配列、マップなどの size() 値は、指定された範囲内である必要があります |
電子メール形式である必要があります | |
@Length | 長さは指定された範囲内である必要があります |
@NotBlank | 文字列を null にすることはできません。また、trim() の後の文字列を "" に等しくすることはできません。 |
@NotEmpty | 文字列を null にすることはできません。また、size() を null にすることはできません。コレクション、配列、マップなどの値を 0 にすることはできません。文字列 trim() は "" と等しくても構いません |
@Range | 値は指定された範囲内である必要があります範囲 |
@URL | は URL |
でなければなりません
1.2. 定义要参数校验的实体类@Data public class ValidVO { private String id; @Length(min = 6,max = 12,message = "appId长度必须位于6到12之间") private String appId; @NotBlank(message = "名字为必填项") private String name; @Email(message = "请填写正确的邮箱地址") private String email; private String sex; @NotEmpty(message = "级别不能为空") private String level; }
1.3. 定义校验类进行测试@RestController @Slf4j @Validated public class ValidController { @ApiOperation("RequestBody校验") @PostMapping("/valid/test1") public String test1(@Validated @RequestBody ValidVO validVO){ log.info("validEntity is {}", validVO); return "test1 valid success"; } @ApiOperation("Form校验") @PostMapping(value = "/valid/test2") public String test2(@Validated ValidVO validVO){ log.info("validEntity is {}", validVO); return "test2 valid success"; } @ApiOperation("单参数校验") @PostMapping(value = "/valid/test3") public String test3(@Email String email){ log.info("email is {}", email); return "email valid success"; } }
1.4. 测试结果1test1的测试结果 发送值 POST http://localhost:8080/valid/test1 Content-Type: application/json { "id": 1, "level": "12", "email": "47693899", "appId": "ab1c" } 返回值
{ "status": 500, "message": "Validation failed for argument [0] in public java.lang.String com.jianzh6.blog.valid.ValidController.test1(com.jianzh6.blog.valid.ValidVO) with 3 errors: [Field error in object 'validVO' on field 'email': rejected value [47693899]; codes [Email.validVO.email,Email.email,Email.java.lang.String,Email]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [validVO.email,email]; arguments []; default message [email],[Ljavax.validation.constraints.Pattern$Flag;@26139123,.*]; default message [不是一个合法的电子邮件地址]]...", "data": null, "timestamp": 1628239624332 } test2的测试结果 发送值 POST http://localhost:8080/valid/test2 Content-Type: application/x-www-form-urlencoded id=1&level=12&email=476938977&appId=ab1c 返回值
{ "status": 500, "message": "org.springframework.validation.BeanPropertyBindingResult: 3 errors\nField error in object 'validVO' on field 'name': rejected value [null]; codes [NotBlank.validVO.name,NotBlank.name,NotBlank.java.lang.String,NotBlank]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [validVO.name,name]; arguments []; default message [name]]; default message [名字为必填项]...", "data": null, "timestamp": 1628239301951 } test3的测试结果 发送值 POST http://localhost:8080/valid/test3 Content-Type: application/x-www-form-urlencoded email=476938977 返回值
{ "status": 500, "message": "test3.email: 不是一个合法的电子邮件地址", "data": null, "timestamp": 1628239281022 } 1.5. 问题虽然我们之前定义了全局异常拦截器,也看到了拦截器确实生效了,但是
1.6. 将参数异常加入全局异常直接修改之前定义的
@Slf4j @RestControllerAdvice public class RestExceptionHandler { /** * 默认全局异常处理。 * @param e the e * @return ResultData */ @ExceptionHandler(Exception.class) @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) public ResultData<String> exception(Exception e) { log.error("全局异常信息 ex={}", e.getMessage(), e); return ResultData.fail(ReturnCode.RC500.getCode(),e.getMessage()); } @ExceptionHandler(value = {BindException.class, ValidationException.class, MethodArgumentNotValidException.class}) public ResponseEntity<ResultData<String>> handleValidatedException(Exception e) { ResultData<String> resp = null; if (e instanceof MethodArgumentNotValidException) { // BeanValidation exception MethodArgumentNotValidException ex = (MethodArgumentNotValidException) e; resp = ResultData.fail(HttpStatus.BAD_REQUEST.value(), ex.getBindingResult().getAllErrors().stream() .map(ObjectError::getDefaultMessage) .collect(Collectors.joining("; ")) ); } else if (e instanceof ConstraintViolationException) { // BeanValidation GET simple param ConstraintViolationException ex = (ConstraintViolationException) e; resp = ResultData.fail(HttpStatus.BAD_REQUEST.value(), ex.getConstraintViolations().stream() .map(ConstraintViolation::getMessage) .collect(Collectors.joining("; ")) ); } else if (e instanceof BindException) { // BeanValidation GET object param BindException ex = (BindException) e; resp = ResultData.fail(HttpStatus.BAD_REQUEST.value(), ex.getAllErrors().stream() .map(ObjectError::getDefaultMessage) .collect(Collectors.joining("; ")) ); } return new ResponseEntity<>(resp,HttpStatus.BAD_REQUEST); } } 1.7. 测试结果2test1测试结果 发送值 POST http://localhost:8080/valid/test1 Content-Type: application/json { "id": 1, "level": "12", "email": "47693899", "appId": "ab1c" } 接收值 { "status": 400, "message": "名字为必填项; 不是一个合法的电子邮件地址; appId长度必须位于6到12之间", "data": null, "timestamp": 1628435116680 } 2. 自定义注解虽然Spring Validation 提供的注解基本上够用,但是面对复杂的定义,我们还是需要自己定义相关注解来实现自动校验。 比如上面实体类中的sex性别属性,只允许前端传递传 M,F 这2个枚举值,如何实现呢? 2.1. 第一步,创建自定义注解@Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE}) @Retention(RUNTIME) @Repeatable(EnumString.List.class) @Documented @Constraint(validatedBy = EnumStringValidator.class)//标明由哪个类执行校验逻辑 public @interface EnumString { String message() default "value not in enum values."; Class<?>[] groups() default {}; Class<? extends Payload>[] payload() default {}; /** * @return date must in this value array */ String[] value(); /** * Defines several {@link EnumString} annotations on the same element. * * @see EnumString */ @Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE}) @Retention(RUNTIME) @Documented @interface List { EnumString[] value(); } }
2.2. 第二步,自定义校验逻辑public class EnumStringValidator implements ConstraintValidator<EnumString, String> { private List<String> enumStringList; @Override public void initialize(EnumString constraintAnnotation) { enumStringList = Arrays.asList(constraintAnnotation.value()); } @Override public boolean isValid(String value, ConstraintValidatorContext context) { if(value == null){ return true; } return enumStringList.contains(value); } } 2.3. 第三步,在字段上增加注解@ApiModelProperty(value = "性别") @EnumString(value = {"F","M"}, message="性别只允许为F或M") private String sex; 2.4. 第四步,体验效果POST http://localhost:8080/valid/test2 Content-Type: application/x-www-form-urlencoded id=1&name=javadaily&level=12&email=476938977@qq.com&appId=ab1cdddd&sex=N { "status": 400, "message": "性别只允许为F或M", "data": null, "timestamp": 1628435243723 } 3. 分组校验一个VO对象在新增的时候某些字段为必填,在更新的时候又非必填。如上面的 在实际开发中我见到很多同学都是建立两个VO对象,
要使用分组校验,只需要三个步骤 3.1. 第一步,定义分组接口public interface ValidGroup extends Default { interface Crud extends ValidGroup{ interface Create extends Crud{ } interface Update extends Crud{ } interface Query extends Crud{ } interface Delete extends Crud{ } } }
3.2. 第二步,在模型中给参数分配分组@Data public class ValidVO { @Null(groups = ValidGroup.Crud.Create.class) @NotNull(groups = ValidGroup.Crud.Update.class, message = "应用ID不能为空") private String id; @Length(min = 6,max = 12,message = "appId长度必须位于6到12之间") @Null(groups = ValidGroup.Crud.Create.class) @NotNull(groups = ValidGroup.Crud.Update.class, message = "应用ID不能为空") private String appId; @NotBlank(message = "名字为必填项") @NotBlank(groups = ValidGroup.Crud.Create.class,message = "名字为必填项") private String name; @Email(message = "请填写正确的邮箱地址") private String email; @EnumString(value = {"F","M"}, message="性别只允许为F或M") private String sex; @NotEmpty(message = "级别不能为空") private String level; }
3.3. 第三步,给需要参数校验的方法指定分组@PostMapping(value = "/valid/add") public String add(@Validated(value = ValidGroup.Crud.Create.class) ValidVO validVO){ log.info("validEntity is {}", validVO); return "test3 valid success"; } @PostMapping(value = "/valid/update") public String update(@Validated(value = ValidGroup.Crud.Update.class) ValidVO validVO){ log.info("validEntity is {}", validVO); return "test4 valid success"; }
3.4. 测试POST http://localhost:8080/valid/add Content-Type: application/x-www-form-urlencoded name=javadaily&level=12&email=476938977@qq.com&sex=F Create操作 在Create时我们没有传递id和appId参数,校验通过。 { "status": 100, "message": "操作成功", "data": "test3 valid success", "timestamp": 1652186105359 } update操作 使用同样的参数调用update方法时则提示参数校验错误 { "status": 400, "message": "ID不能为空; 应用ID不能为空", "data": null, "timestamp": 1652186962377 } 默认校验生效操作
故意写错email格式 POST http://localhost:8080/valid/add Content-Type: application/x-www-form-urlencoded /valid/update?name=javadaily&level=12&email=476938977&sex=F { "status": 400, "message": "请填写正确的邮箱地址; ID不能为空; 应用ID不能为空", "data": null, "timestamp": 1652187273865 } 4. 业务规则校验业务规则校验指接口需要满足某些特定的业务规则,举个例子:业务系统的用户需要保证其唯一性,用户属性不能与其他用户产生冲突,不允许与数据库中任何已有用户的用户名称、手机号码、邮箱产生重复。 这就要求在创建用户时需要校验用户名称、手机号码、邮箱是否被注册;编辑用户时不能将信息修改成已有用户的属性。
4.1. 自定义注解首先我们需要创建两个自定义注解,用于业务规则校验:
4.2. 实现业务校验规则
接口的第一个参数是 自定义注解类型,第二个参数是 被注解字段的类,因为需要校验多个参数,我们直接传入用户对象。需要提到的一点是 @Slf4j public class UserValidation<T extends Annotation> implements ConstraintValidator<T, User> { protected Predicate<User> predicate = c -> true; @Resource protected UserRepository userRepository; @Override public boolean isValid(User user, ConstraintValidatorContext constraintValidatorContext) { return userRepository == null || predicate.test(user); } /** * 校验用户是否唯一 * 即判断数据库是否存在当前新用户的信息,如用户名,手机,邮箱 */ public static class UniqueUserValidator extends UserValidation<UniqueUser>{ @Override public void initialize(UniqueUser uniqueUser) { predicate = c -> !userRepository.existsByUserNameOrEmailOrTelphone(c.getUserName(),c.getEmail(),c.getTelphone()); } } /** * 校验是否与其他用户冲突 * 将用户名、邮件、电话改成与现有完全不重复的,或者只与自己重复的,就不算冲突 */ public static class NotConflictUserValidator extends UserValidation<NotConflictUser>{ @Override public void initialize(NotConflictUser notConflictUser) { predicate = c -> { log.info("user detail is {}",c); Collection<User> collection = userRepository.findByUserNameOrEmailOrTelphone(c.getUserName(), c.getEmail(), c.getTelphone()); // 将用户名、邮件、电话改成与现有完全不重复的,或者只与自己重复的,就不算冲突 return collection.isEmpty() || (collection.size() == 1 && collection.iterator().next().getId().equals(c.getId())); }; } } }
4.3. 测试代码@RestController @RequestMapping("/senior/user") @Slf4j @Validated public class UserController { @Autowired private UserRepository userRepository; @PostMapping public User createUser(@UniqueUser @Valid User user){ User savedUser = userRepository.save(user); log.info("save user id is {}",savedUser.getId()); return savedUser; } @SneakyThrows @PutMapping public User updateUser(@NotConflictUser @Valid @RequestBody User user){ User editUser = userRepository.save(user); log.info("update user is {}",editUser); return editUser; } }
POST http://localhost:8080/valid/add Content-Type: application/json /senior/user { "userName" : "100001" } { "status": 400, "message": "用户名、手机号码、邮箱不允许与现存用户重复", "data": null, "timestamp": 1652196524725 } |
以上がSpringBoot パラメータ検証 Validator フレームワークの使用方法の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

ホットAIツール

Undresser.AI Undress
リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover
写真から衣服を削除するオンライン AI ツール。

Undress AI Tool
脱衣画像を無料で

Clothoff.io
AI衣類リムーバー

AI Hentai Generator
AIヘンタイを無料で生成します。

人気の記事

ホットツール

WebStorm Mac版
便利なJavaScript開発ツール

メモ帳++7.3.1
使いやすく無料のコードエディター

AtomエディタMac版ダウンロード
最も人気のあるオープンソースエディター

SecLists
SecLists は、セキュリティ テスターの究極の相棒です。これは、セキュリティ評価中に頻繁に使用されるさまざまな種類のリストを 1 か所にまとめたものです。 SecLists は、セキュリティ テスターが必要とする可能性のあるすべてのリストを便利に提供することで、セキュリティ テストをより効率的かつ生産的にするのに役立ちます。リストの種類には、ユーザー名、パスワード、URL、ファジング ペイロード、機密データ パターン、Web シェルなどが含まれます。テスターはこのリポジトリを新しいテスト マシンにプルするだけで、必要なあらゆる種類のリストにアクセスできるようになります。

SAP NetWeaver Server Adapter for Eclipse
Eclipse を SAP NetWeaver アプリケーション サーバーと統合します。
