我正在构建一个Spring Boot应用程序,并试图为我将在服务层验证的一些DTO/实体实现自定义验证。基于Spring关于这个问题的文档,我认为一种方法是实现org。springframework。验证。验证程序界面。
作为一个最小、完整、可重复的示例,请考虑以下代码:
Spring初始化引导项目
在src/main/java/com中添加了以下代码。实例使用验证:
// Person.java
package com.example.usingvalidation;
public class Person {
private String firstName;
private String lastName;
private int age;
private String gender;
public Person() {
}
public Person(String firstName, String lastName, int age, String gender) {
this.firstName = firstName;
this.lastName = lastName;
this.age = age;
this.gender = gender;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
@Override
public String toString() {
return "Person{" +
"firstName='" + firstName + '\'' +
", lastName='" + lastName + '\'' +
", age=" + age +
", gender='" + gender + '\'' +
'}';
}
}
// PersonValidator.java
package com.example.usingvalidation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.validation.Errors;
import org.springframework.validation.Validator;
@Component
public class PersonValidator implements Validator {
private final Logger log = LoggerFactory.getLogger(this.getClass());
@Override
public boolean supports(Class<?> clazz) {
log.info("supports called");
return Person.class.equals(clazz);
}
@Override
public void validate(Object target, Errors errors) {
log.info("validate called");
Person person = (Person) target;
errors.reject("E00001", "This is the default error message, just to test.");
}
}
// MyController.java
package com.example.usingvalidation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import javax.validation.ConstraintViolation;
import java.util.Set;
@RestController
public class MyController {
private final Logger log = LoggerFactory.getLogger(this.getClass());
private final LocalValidatorFactoryBean validatorFactory;
@Autowired
public MyController(LocalValidatorFactoryBean validatorFactory) {
this.validatorFactory = validatorFactory;
}
@GetMapping("/")
public Person getPerson(@RequestBody Person person) {
log.info("calling validate");
Set<ConstraintViolation<Person>> errors = validatorFactory.validate(person);
log.info("called validate, result: {}", errors);
return null;
}
}
// UsingValidationApplication.java nothing changed here
package com.example.usingvalidation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class MyController {
private final Logger log = LoggerFactory.getLogger(this.getClass());
private final LocalValidatorFactoryBean validatorFactory;
@Autowired
public MyController(LocalValidatorFactoryBean validatorFactory) {
this.validatorFactory = validatorFactory;
}
@GetMapping("/")
public Person getPerson(@RequestBody Person person) {
log.info("calling validate");
validatorFactory.validate(person);
return null;
}
}
如果我点击endpoint触发验证,则不会发生任何事情。我看到调用validate日志消息。但errors对象为空。PersonValidator中的所有日志消息都没有被记录,因此很明显那里没有呼叫。
我的问题是:如何向Spring注册验证器,以便使用验证器?
我已经多次阅读了这些文档和数百个SO问题(比如java——使用JSR-303和spring的验证器堆栈溢出的组合为Spring Bootendpoint实现自定义验证逻辑),但都无济于事。
@NotNull
这样的JSR-303注释,那么当前设置将拾取与JSR-303验证相关的错误。但这不是我需要的,我需要它来使用我的自定义验证器。InitBinder
在控制器中用于向Spring注册验证器。但我不想这样做,因为我计划在服务层进行这些自定义验证。您应该使用已验证的注释对控制器进行注释。不要忘记将spring boot starter验证添加到pom中。
这对您不起作用的主要原因是您尚未向DataBinder注册验证器。
对您的控制器进行一些更改。不要自动连接LocalValidatorFactoryBean
,而是自动将您的验证器连接到控制器并将它们注册到DataBinder。
@Autowired
private PersonValidator personValidator;
@InitBinder
public void initBinder(WebDataBinder binder) {
binder.addValidators(personValidator);
}
您的控制器方法也会更简单,因为您不再需要显式调用ValidatorFactory,当您向方法参数添加@Valid注释时,Spring将自动调用验证器。将BindingResult参数添加到方法中,来自验证器的所有错误都将出现在BindingResult错误中,这包括由javax验证引起的错误,如@Min、@Max、@NotNull等。
@GetMapping("/")
public Person getPerson(@RequestBody @Valid Person person, BindingResult bindingResult) {
if (bindingResult.hasErrors()) {
log.info(bindingResult.getAllErrors());
}
return null;
}
由于您希望在服务层中执行此操作,因此必须编写自己的逻辑来处理此操作。Spring在调用自定义验证方面没有任何魔力。这是有意的,进入应用程序是通过控制器进行的,这是您对输入数据控制有限的地方,因此如果您想验证,应该在这里进行处理。您完全控制的控制器下游的Person对象的每一个突变。如果您觉得必须在服务层进行验证,那么您将自己编写这篇文章,坦率地说,我不会为此使用Spring的验证器实现。如果您对在服务层中执行此操作毫无兴趣,这里有一种方法可以实现。
创建一个注释以应用于您的个人类
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import javax.validation.Constraint;
@Documented
@Constraint(validatedBy = PersonValidator.class)
@Target({TYPE})
@Retention(RUNTIME)
public @interface ValidPerson {
String message() default "This isn't correct";
Class[] groups() default {};
Class[] payload() default {};
}
将上述注释添加到您的Person类中。
@ValidPerson
public class Person {
将您的Person Validator修改为ConstraintValidator
,我已经组合了一个实现来验证Person上的两个字段。
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import org.springframework.util.ObjectUtils;
public class PersonValidator implements ConstraintValidator<ValidPerson, Person> {
@Override
public void initialize(ValidPerson constraintAnnotation) {
ConstraintValidator.super.initialize(constraintAnnotation);
}
@Override
public boolean isValid(Person value, ConstraintValidatorContext context) {
boolean isErrored = false;
if (ObjectUtils.isEmpty(value.getLastName())) {
context.disableDefaultConstraintViolation();
context.buildConstraintViolationWithTemplate("lastName can't be empty").addConstraintViolation();
isErrored = true;
}
if (value.getAge() < 0) {
context.disableDefaultConstraintViolation();
context.buildConstraintViolationWithTemplate("You're not old enough to be alive").addConstraintViolation();
isErrored = true;
}
return !isErrored;
}
}
在您的服务类中,注入一个验证器并在方法中调用它,这将调用您定义并添加到人员中的约束验证程序
import java.util.Set;
import javax.validation.ConstraintViolation;
import javax.validation.Validator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class PersonService {
@Autowired
private Validator validator;
public Person updatePerson(Person person) {
Set<ConstraintViolation<Person>> validate = validator.validate(person);
return person;
}
}
您可以使用AOP做一些花哨的事情来自动执行此操作,就像Spring在控制器方面所做的那样,但我会让您自己去发现。
问题内容: 我有其他人编写的REST api,其中处理请求到特定url的方法接受一堆从路径参数填充的参数。 撰写此文章的人曾经使用过DropWizard,但我以前没有使用它的经验。我的任务是通过将其与db中的值进行比较来验证studentId字段。这将非常简单,但是我被告知要使用自定义验证程序来执行此操作。我对编写注释非常陌生,但经过大量挖掘后,这样编写了注释, 此后,我将注释添加到了要像这样运行
问题内容: 我在这里尝试了答案中的步骤:HibernateValidator,自定义ResourceBundleLocator和Spring 但是仍然只是作为输出而不是消息。 dispatcher-servlet.xml /WEB-INF/validationMessages.properties: 表格(上课地点) 这是怎么了 问题答案: 得到它了!:-) 我在我的计算机中添加了以下bean而不
我想确保在创建新用户名或更新用户名时,用户名是唯一的。我写了下面的代码,它可以很好地创建和更新用户名。但是,如果只更新其他用户字段,如年龄或性别,并保留相同的用户名,它将返回invalid,因为用户名已经存在。 重要的是,我想要的验证是使用BindingResault。我已经有一个数据库,将用户名视为唯一的限制。但是现在我想要用BindingResault进行同样的验证。但是我更新用户时的方式会导
我有一个自定义组件在我的应用程序上继承ControlValueAccessor,它集成了ng-select 这个组件的目标是有一个中心位置来集成ng-select,这样我就可以在整个应用程序中重用它。 这就是我调用验证方法的方式: 这就是我通常在父组件上实例化formControl的方式: 我想给父级一个选项,在我的自定义组件上使用内置的角度验证器。(必填项,电子邮件,最小值,最大值……)。 问题
问题内容: 我正在使用带有自定义验证的Angular2的FormBuilder开发表单。问题:在customValidator中,我用来访问本地对象。执行验证时出现错误。 看起来customValidator是在其他对象中执行的,因此更改了引用 问题: 如何传递对customValidator 的引用? 问题答案: 使用箭头函数,以确保该函数绑定到此:
我正在尝试编写一个自定义bean验证器,并根据用户界面上的语言环境显示验证消息。 为此,我创建了一个验证器,如下所示: 我还注册了messageSource和validator bean: 在我的控制器中,我使用initBinder注册我的验证器: 不过,验证错误消息在用户界面上显示为{myproject.myclass.validation.name}。即使我设置了LocalValidatorF