- Validation校检
- 认识
- 基本使用
- 验证 Spring MVC 控制器的输入
- 验证 service层输入
- 验证持久化层-实体输入
- 使用验证组为不同的用例验证不同的对象
- 自定义验证错误
- 自定义验证器
- 以编程方式验证
- 验证反模式(使用问题)
- 仅在持久层中进行验证
- 使用霰弹式验证
- 使用验证组进行用例验证
Validation校检
教程学习 - reflectoring.io
反模式学习 - reflectoring.io
认识
基本使用
验证 Spring MVC 控制器的输入
-
验证请求正文
- 如果不满足条件则会触发
MethodArgumentNotValidException
异常 - 使用
@Valid
校检规则会生效
public class Input {
@Min(10)
@Max(30)
private int age;
}
@RestController
class ValidateRequestBodyController {
@PostMapping("/validateBody")
ResponseEntity<String> validateBody(@Valid @RequestBody Input input) {
return ResponseEntity.ok("valid");
}
}
-
验证路径变量和请求参数
@RestController
@Validated
public class ValidateRequestBodyController {
@GetMapping("/validatePathVariable/{id}")
ResponseEntity<String> validatePathVariable( @PathVariable("id") @Min(5) int id) {
return ResponseEntity.ok("valid");
}
@GetMapping("/validateRequestParameter")
ResponseEntity<String> validateRequestParameter(@RequestParam("param") @Min(5) int param) {
return ResponseEntity.ok("valid");
}
@ExceptionHandler(ConstraintViolationException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
ResponseEntity<String> handleConstraintViolationException(ConstraintViolationException e) {
return new ResponseEntity<>( "validation error: " + e.getMessage(), HttpStatus.BAD_REQUEST);
}
}
验证 service层输入
-
结合@Validated
和@Valid
,可以验证任何 Spring 组件的输入
@Service
@Validated
class ValidatingService{
void validateInput(@Valid Input input){
}
}
@ExtendWith(SpringExtension.class)
@SpringBootTest
class ValidatingServiceTest {
@Autowired
private ValidatingService service;
@Test
void whenInputIsInvalid_thenThrowsException(){
assertThrows(ConstraintViolationException.class, () -> {
service.validateInput(input);
});
}
}
验证持久化层-实体输入
使用验证组为不同的用例验证不同的对象
自定义验证错误
自定义验证器
-
用于扩展验证
-
步骤:
-
自定义约束注解IpAddress
@Target({ FIELD })
@Retention(RUNTIME)
@Constraint(validatedBy = IpAddressValidator.class)
@Documented
public @interface IpAddress {
String message() default "{IpAddress.invalid}";
Class<?>[] groups() default { };
Class<? extends Payload>[] payload() default { };
}
-
验证器实现
class IpAddressValidator implements ConstraintValidator<IpAddress, String> {
@Override
public boolean isValid(String value, ConstraintValidatorContext context) {
Pattern pattern =
Pattern.compile("^([0-9]{1,3})\\.([0-9]{1,3})\\.([0-9]{1,3})\\.([0-9]{1,3})$");
Matcher matcher = pattern.matcher(value);
try {
if (!matcher.matches()) {
return false;
} else {
for (int i = 1; i <= 4; i++) {
int octet = Integer.valueOf(matcher.group(i));
if (octet > 255) {
return false;
}
}
return true;
}
} catch (Exception e) {
return false;
}
}
}
-
使用
@IpAddress
像使用任何其他约束注解一样使用注解
class InputWithCustomValidator {
@IpAddress
private String ipAddress;
}
以编程方式验证
验证反模式(使用问题)
仅在持久层中进行验证
-
持久层为最底层
如:mvc架构,web层(controller) -> 服务层(service) -> 持久层(mapper/dao)
-
只在持久层中进行验证,会导致一些无效的数据传入到了web层和业务层(服务层),因为这两层并没有进行校检。
从而在业务层产生一些错误,因此应当在业务层进行校检。如果需要在持久层进行进一步校检,充当安全网
使用霰弹式验证
-
散弹式验证:到处添加@Validation验证,不管他会不会得到验证。
-
这种方式的验证会导致减低开发速度、减低可读性
- 如:当别人看到这段代码时,会思考为什么这么设置?他一定是有原因的。但是其实他并没有用处,只是习惯性的添加验证。
因此在使用@Validation验证时,需要思考这是不是必要的?
-
当到处都是这种验证时,如果遇到意外的验证错误,不会那么容易找到触发验证的位置。这时就会导致浪费大量的时间
使用验证组进行用例验证
- 验证组违反了单一职责原则,模型类需要知道所有验证规则。如果用于特定用例的验证发生更改,则模型类必须更改。
- 当验证组越来越多时,会很难阅读,因为需要了解他相关的逻辑
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)