假设我不想使用JSR303来验证我的bean。当bean被标记为有效时,是否可以实现Spring将使用的自定义验证器?
如果我的自定义验证器是Spring组件,那就太好了
您必须实现Validator
接口并将实现标记为@Component
。然后您可以使用@InitBinder
注释绑定您的自定义验证器。类似于这样:
@Component
public class MyCustomValidator implements Validator
{
@Override
public boolean supports(Class<?> clazz)
{
// your custom logic
}
@Override
public void validate(Object target, Errors errors)
{
// your custom logic
}
}
要将其与注释绑定,请执行以下操作:
@Controller
public class MyController
{
@Autowired
private MyCustomValidator validator;
@InitBinder
protected void initBinder(final WebDataBinder binder)
{
binder.addValidators(validator);
}
}
@fedor.belov
我在另一个问题中详述了这个问题。在我的需要中,我想保留JSR-303并使自定义验证器工作,但您可以更改我的示例代码以满足您的需要。
这个问题可以通过扩展LocalValidatorFactoryBean来解决,您可以重写这个类中的validate方法,提供您想要的任何行为。
在我的情况下,我需要在同一个控制器中的不同方法中使用JSR-303和同一模型的自定义验证器,通常建议使用@InitBinder,但这对于我的情况是不够的,因为InitBinder在模型和验证器之间进行绑定(如果使用@RequestBody InitBinder,则只针对一个模型和每个控制器一个验证器)。
控制器
@RestController
public class LoginController {
@PostMapping("/test")
public Test test(@Validated(TestValidator.class) @RequestBody Test test) {
return test;
}
@PostMapping("/test2")
public Test test2(@Validated @RequestBody Test test) {
return test;
}
}
自定义验证器
public class TestValidator implements org.springframework.validation.Validator {
@Override
public boolean supports(Class<?> clazz) {
return Test.class.isAssignableFrom(clazz);
}
@Override
public void validate(Object target, Errors errors) {
Test test = (Test) target;
errors.rejectValue("field3", "weird");
System.out.println(test.getField1());
System.out.println(test.getField2());
System.out.println(test.getField3());
}
}
要验证的类
public class Test {
@Size(min = 3)
private String field2;
@NotNull
@NotEmpty
private String field1;
@NotNull
@Past
private LocalDateTime field3;
//...
//getter/setter
//...
}
自定义本地验证器工厂Bean
public class CustomLocalValidatorFactoryBean extends LocalValidatorFactoryBean {
private Logger logger = LoggerFactory.getLogger(this.getClass());
@Override
public void validate(@Nullable Object target, Errors errors, @Nullable Object... validationHints) {
Set<Validator> concreteValidators = new LinkedHashSet<>();
Set<Class<?>> interfaceGroups = new LinkedHashSet<>();
extractConcreteValidatorsAndInterfaceGroups(concreteValidators, interfaceGroups, validationHints);
proccessConcreteValidators(target, errors, concreteValidators);
processConstraintViolations(super.validate(target, interfaceGroups.toArray(new Class<?>[interfaceGroups.size()])), errors);
}
private void proccessConcreteValidators(Object target, Errors errors, Set<Validator> concreteValidators) {
for (Validator validator : concreteValidators) {
validator.validate(target, errors);
}
}
private void extractConcreteValidatorsAndInterfaceGroups(Set<Validator> concreteValidators, Set<Class<?>> groups, Object... validationHints) {
if (validationHints != null) {
for (Object hint : validationHints) {
if (hint instanceof Class) {
if (((Class<?>) hint).isInterface()) {
groups.add((Class<?>) hint);
} else {
Optional<Validator> validatorOptional = getValidatorFromGenericClass(hint);
if (validatorOptional.isPresent()) {
concreteValidators.add(validatorOptional.get());
}
}
}
}
}
}
@SuppressWarnings("unchecked")
private Optional<Validator> getValidatorFromGenericClass(Object hint) {
try {
Class<Validator> clazz = (Class<Validator>) Class.forName(((Class<?>) hint).getName());
return Optional.of(clazz.newInstance());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {
logger.info("There is a problem with the class that you passed to "
+ " @Validated annotation in the controller, we tried to "
+ " cast to org.springframework.validation.Validator and we cant do this");
}
return Optional.empty();
}
}
配置应用程序
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
@Bean
public javax.validation.Validator localValidatorFactoryBean() {
return new CustomLocalValidatorFactoryBean();
}
}
输入到/test
endpoint:
{
"field1": "",
"field2": "aaaa",
"field3": "2018-04-15T15:10:24"
}
来自测试endpoint的输出:
{
"timestamp": "2018-04-16T17:34:28.532+0000",
"status": 400,
"error": "Bad Request",
"errors": [
{
"codes": [
"weird.test.field3",
"weird.field3",
"weird.java.time.LocalDateTime",
"weird"
],
"arguments": null,
"defaultMessage": null,
"objectName": "test",
"field": "field3",
"rejectedValue": "2018-04-15T15:10:24",
"bindingFailure": false,
"code": "weird"
},
{
"codes": [
"NotEmpty.test.field1",
"NotEmpty.field1",
"NotEmpty.java.lang.String",
"NotEmpty"
],
"arguments": [
{
"codes": [
"test.field1",
"field1"
],
"arguments": null,
"defaultMessage": "field1",
"code": "field1"
}
],
"defaultMessage": "Não pode estar vazio",
"objectName": "test",
"field": "field1",
"rejectedValue": "",
"bindingFailure": false,
"code": "NotEmpty"
}
],
"message": "Validation failed for object='test'. Error count: 2",
"path": "/user/test"
}
输入到endpoint:
{
"field1": "",
"field2": "aaaa",
"field3": "2018-04-15T15:10:24"
}
输出到测试2的endpoint:
{
"timestamp": "2018-04-16T17:37:30.889+0000",
"status": 400,
"error": "Bad Request",
"errors": [
{
"codes": [
"NotEmpty.test.field1",
"NotEmpty.field1",
"NotEmpty.java.lang.String",
"NotEmpty"
],
"arguments": [
{
"codes": [
"test.field1",
"field1"
],
"arguments": null,
"defaultMessage": "field1",
"code": "field1"
}
],
"defaultMessage": "Não pode estar vazio",
"objectName": "test",
"field": "field1",
"rejectedValue": "",
"bindingFailure": false,
"code": "NotEmpty"
}
],
"message": "Validation failed for object='test'. Error count: 1",
"path": "/user/test2"
}
原始答案和原始问题。
我希望这能有所帮助。
我想验证属性firstName和lastName。但我不想每次都重复注释。 如何创建自定义注释,使代码如下所示 验证也是一样的
我有一个使用javax注释的jax-rs项目(
在尝试使用JSR-303(hibernate Validator)验证模型时,我在将
假设我有以下课程: 是否可以通过“MyProduct”类验证“code”属性?比如:
我对Spring靴有疑问。如何对Spring转换器进行验证?我试图从javax使用ConstraintValidator,但它在转换器之后运行。
我试图使用swagger来记录我的Restapi,并且取得了一些成功,但是在受限资源参数方面,我遇到了一堵墙。我正在使用JBoss RESTEasy与Hibernate验证器提供程序和Jackson 2.x注释的POJO。我遇到的问题是,当遇到@DecimalMin/@DecimalMax注释时,我得到一个 似乎swagger正在尝试使用较新版本的javax.validation.constrai