我可以从Spring Boot的服务中明确调用自定义验证器吗?

huangapple go评论80阅读模式
英文:

Can I explicitly call custom validator from service in Spring Boot?

问题

我有一个自定义验证器类,实现了 Validator 接口,如下所示:

 public class MyCustomValidator implements Validator

我想要能够从一个服务中调用它的 validate() 方法。
这个方法的代码如下:

@Override
public void validate(Object target, Errors errors) {
     // 验证逻辑在这里
     MyClass request = (MyClass) target;
     if (request.getId() == null) {
         errors.reject("content.id", "Id is missing");
     }
}

我不想将这个验证器放在我的终端点(endpoint)中,因为我需要从数据库中获取要验证的对象,然后对其进行验证,所以我需要从我的服务中进行操作。

你能否请指导我如何实现这一点?

英文:

I have a custom validator class that implements Validator, like this:

 public class MyCustomValidator implements Validator

I want to be able to call its validate() method from a Service.
This is how this method looks:

@Override
public void validate(Object target, Errors errors) {
     // validation goes here
     MyClass request = (MyClass) target;
     if (request.getId() == null) {
         errors.reject("content.id", "Id is missing";
     }
}

I don't want to have this validator in my endpoint, because I need to fetch the object to be validated from the database and then call the validation on it, so I need to do it from my service.

Can you please guide me on how to achieve this?

答案1

得分: 2

在类中使用验证注解,但在请求体上不使用 @Valid,这样 Spring 将不会验证您的类。

public class MyClass {
   
   @NotNull
   private Integer id;
   
   @NotBlank
   private String data;
}

首先自动装配 Validator

@Autowired
private final Validator validator;

然后对类进行验证,根据需要在条件满足时使用验证器。

if (isValidate) {
    Set<ConstraintViolation<MyClass>> violations = validator.validate(myClassObj);
    if (!violations.isEmpty()) {
        throw new ConstraintViolationException(new HashSet<ConstraintViolation<?>>(violations));
    }
}
英文:

Use validation annotations in class but don't use @Valid on request body, then spring won't validate your class.

public class MyClass{
   
   @NotNull
   private Integer id;
   
   @NotBlank
   private String data;
}

Autowired Validator first

@Autowired
private final Validator validator;

Then for class validate using the validator conditionally when needed.

if(isValidate) {
    Set&lt;ConstraintViolation&lt;MyClass&gt;&gt; violations = validator.validate(myClassObj);
    if (!violations.isEmpty()) {
      throw new ConstraintViolationException(new HashSet&lt;ConstraintViolation&lt;?&gt;&gt;(violations));
    }

}

答案2

得分: 0

你可以注入 Validator 并调用 validate 方法:

@Autowired
Validator validator;

然后调用 validate 方法:

Set<ConstraintViolation<Driver>> violations = validator.validate(yourObjectToValidate);
英文:

You could inject Validator and call validate

@Autowired
Validator validator;

And then call validate:

Set&lt;ConstraintViolation&lt;Driver&gt;&gt; violations = validator.validate(yourObjectToValidate);

答案3

得分: 0

以下是翻译好的内容:

验证器接口是根据public boolean Validator.supports(Class clazz)方法确定的匹配对象(据我理解)一旦调用。

然而,您的目标似乎是仅在特定时间验证来自持久层到服务层的MyClass对象。

有多种实现这一目标的方法。

第一种,也是最明显的一种,是不要扩展任何类,而是使用具有某种验证功能概念的自定义组件:

@Component
public class CustomValidator{
    public void validate(MyClass target) throws ValidationException {
        // 验证逻辑在此处
        if (target.getId() == null) {
            throw new ValidationException("Id is missing");
        }
    }
}

并将其注入/自动装配到您的服务对象中:

@Component
public class MyClassService{
    // 将在此组件的第一个实例中注入
    @Autowired
    private CustomValidator validator;

    public MyClass get(MyClass target) {
        try {
            validator.validate(target);
            return dao.retrieve(target);
        } catch (ValidationException) {
            // 处理验证错误
        } catch (DataAccessException) {
            // 处理数据访问异常
        }
    }
}

这种方法的好处是您可以自己控制验证和错误处理。不足之处是相对较高的样板代码。

然而,如果您希望为不同的CRUD操作(或服务方法)使用不同的验证器,您可能会对Spring Validation Groups Feature感兴趣。

首先,为您想要区分的每个操作创建一个简单的标记接口:

interface OnCreate {};
interface OnUpdate {};

然后,您只需在实体类的字段中使用这些标记接口,使用Bean Validation Annotations

public class MyClass{
    @Null(groups = OnCreate.class)
    @NotNull(groups = OnUpdate.class)
    String id;
}

为了在Service类中使用这些分组,您将需要使用@Validated注解。

@Validated
@Service
public class MyService {
    @Validated(OnCreate.class)
    void validateForCreate(@Valid InputWithGroups input){
      // 做一些操作
    }

    @Validated(OnUpdate.class)
    void validateForUpdate(@Valid InputWithGroups input){
      // 做一些操作
    }
}

请注意,@Validated不仅适用于方法,还适用于Service类。如果您计划使用多个服务,也可以为整个服务设置分组。

个人而言,我大多数情况下使用内置的Jakarta Bean Validation注解与标记接口相结合,因为它们易于使用,几乎没有样板代码,同时保持了一定的灵活性和可调整性。

英文:

The Validator interface is, as far as i understand it, called as soon as a matching object (determined by the public boolean Validator.supports(Class clazz) method).

However, your goal seems to be to validate an object of MyClass only at a specific time, coming from your persistence layer to your service layer.

There are multiple ways to achieve this.

The first and most obvious one is to not extend any classes, but to use a custom component with some notion of a validation function:

<!-- language: lang-java -->

@Component
public class CustomValidator{
    public void validate(MyClass target) throws ValidationException {
        // validation goes here
        if (target.getId() == null) {
             throw new ValidationException(&quot;Id is missing&quot;);
        }
    }
}

And inject/autowire it into your service object:

<!-- language: lang-java -->

@Component
public class MyClassService{
    // will be injected in first instance of this component
    @Autowired
    private CustomValidator validator

    public MyClass get(MyClass target) {
        try {
             validator.validate(target);
             return dao.retrieve(target);
        } catch (ValidationException) {
             // handle validation error
        } catch (DataAccessException) {
             // handle dao exception
        }

    }
}

This has the benefit that you yourself can control the validation, and error handling.
The negative side is the relatively high boilerplate.

However, if you want different Validators for different CRUD-Operations (or Service Methods), you may be interested in the Spring Validation Groups Feature.

First, you create a simple marker interface for each Operation you want to differ:

interface OnCreate {};
interface OnUpdate {};

Then, all you need to do is use the marker interfaces in the fields of your entity class,
using the Bean Validation Annotations:

public class MyClass{
    @Null(groups = OnCreate.class)
    @NotNull(groups = OnUpdate.class)
    String id;
}

In order to use those groups in your Service Class, you will have to use the @Validated annotation.

@Validated
@Service
public class MyService {
    @Validated(OnCreate.class)
    void validateForCreate(@Valid InputWithGroups input){
      // do something
    }

    @Validated(OnUpdate.class)
    void validateForUpdate(@Valid InputWithGroups input){
      // do something
    }
}

Note that @Validated is applied to the service class as well as the methods. You can also set the group for the whole service, if you plan on using multiple services.

I for once mostly use the built-in Jakarta Bean Validation annotations in combination with marker interfaces, because of their ease of use and almost no boilerplate, while staying somewhat flexible and adjustable.

huangapple
  • 本文由 发表于 2020年5月4日 19:31:19
  • 转载请务必保留本文链接:https://go.coder-hub.com/61591228.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定