【问题标题】:How to manually trigger spring validation?如何手动触发弹簧验证?
【发布时间】:2015-04-26 11:45:34
【问题描述】:

POJO 字段的带注释的 spring 验证在从 json 请求正文创建时起作用。但是,当我手动(使用 setter)创建同一个对象并想要触发验证时,我不知道该怎么做。

这里是 Registration 类,它有 Builder 内部类,可以构建对象。在构建方法中,我想触发弹簧验证。请滚动到底部并检查 Builder.build() 和 Builder.valiate() 方法以查看当前实现。我正在使用 javax.validation.Validator 来触发验证,但如果可能的话,我更喜欢利用 spring 验证。

package com.projcore.dao;

import com.projcore.util.ToString;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import org.hibernate.validator.constraints.NotEmpty;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.validation.ConstraintViolation;
import javax.validation.Valid;
import javax.validation.Validation;
import javax.validation.Validator;
import javax.validation.constraints.Size;
import java.util.List;
import java.util.Set;

/**
 * The data transfer object that contains the information of a Registration
 * and validation rules for attributes.
 */
@JsonIgnoreProperties(ignoreUnknown = true)
public final class Registration {
    private static final Logger LOGGER = LoggerFactory.getLogger(Registration.class);

    private String id;

    @NotEmpty
    @Size(max = 255)
    private String messageId;

    @NotEmpty
    @Size(max = 255)
    private String version;

    @Size(max = 255)
    private String system;

    public Registration() {
    }

    private Registration(Builder builder) {
        this.id = builder.id;
        this.messageId = builder.messageId;
        this.version = builder.version;
        this.system = builder.system;
    }

    public static Builder getBuilder() {
        return new Builder();
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getMessageId() {
        return messageId;
    }

    public void setMessageId(String messageId) {
        this.messageId = messageId;
    }

    public String getVersion() {
        return version;
    }

    public void setVersion(String version) {
        this.version = version;
    }

    public String getSystem() {
        return system;
    }

    public void setSystem(String system) {
        this.system = system;
    }

    @Override
    public String toString() {
        return ToString.create(this);
    }

    /**
     * Builder pattern makes the object easier to construct in one line.
     */
    public static class Builder {

        private String id;

        private String messageId;

        private String version;

        private String system;

        private Builder() {}

        public Builder id(String id) {
            this.id = id;
            return this;
        }

        public Builder messageId(String messageId) {
            this.messageId = messageId;
            return this;
        }


        public Builder version(String version) {
            this.version = version;
            return this;
        }

        public Builder system(String system) {
            this.system = system;
            return this;
        }

        public Registration build() {
            Registration entry = new Registration(this);

            // *** Would like to trigger spring validation here ***
            Set violations = validate(entry);
            if (violations.isEmpty())
                return entry;
            else
                throw new RuntimeException(violations.toString());
        }

        private Set validate(Registration entry) {
            Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
            Set<ConstraintViolation<Registration>> constraintViolations = validator.validate(entry);
            return constraintViolations;
        }

    }
}

验证在这里工作正常:

@RequestMapping(method = RequestMethod.POST)
@ResponseStatus(HttpStatus.CREATED)
Registration create(@RequestBody @Valid Registration registration) 

解决办法:

删除了Registraion.Builder.validate()。将 Registryaion.Builder.build() 更新为:

    public Registration build() {
        Registration entry = new Registration(this);
        return (Registration) ValidatorUtil.validate(entry);
    }

ValidationUtil.java

package projcore.util;

import com.ericsson.admcore.error.InvalidDataException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.validation.BeanPropertyBindingResult;
import org.springframework.validation.Errors;
import org.springframework.validation.beanvalidation.SpringValidatorAdapter;

import javax.validation.Validation;
import javax.validation.Validator;
import java.util.Set;

public class ValidatorUtil {
    private static final Logger LOGGER = LoggerFactory.getLogger(ValidatorUtil.class);
    private static final Validator javaxValidator = Validation.buildDefaultValidatorFactory().getValidator();
    private static final SpringValidatorAdapter validator = new SpringValidatorAdapter(javaxValidator);

    public static Object validate(Object entry) {
        Errors errors = new BeanPropertyBindingResult(entry, entry.getClass().getName());
        validator.validate(entry, errors);
        if (errors == null || errors.getAllErrors().isEmpty())
            return entry;
        else {
            LOGGER.error(errors.toString());
            throw new InvalidDataException(errors.getAllErrors().toString(), errors);
        }
    }
}

InvalidDataException.java

package projcore.error;

import org.springframework.validation.Errors;

/**
 * This exception is thrown when the dao has invalid data.
 */
public class InvalidDataException extends RuntimeException {
    private Errors errors;

    public InvalidDataException(String msg, Errors errors) {
        super(msg);
        setErrors(errors);
    }

    public Errors getErrors() {
        return errors;
    }

    public void setErrors(Errors errors) {
        this.errors = errors;
    }
}

【问题讨论】:

    标签: java spring spring-boot bean-validation


    【解决方案1】:

    Spring 完全支持 JSR-303 Bean Validation API。这包括对将 JSR-303 实现引导为 Spring bean 的便捷支持。这允许在您的应用程序中需要验证的任何地方注入 javax.validation.Validator。

    使用 LocalValidatorFactoryBean 将默认的 JSR-303 Validator 配置为 Spring bean:

       <bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean" />
    

    上述基本配置将触发 JSR-303 使用其默认引导机制进行初始化。 JSR-303 提供程序(例如 Hibernate Validator)预计会出现在类路径中,并且会被自动检测到。

    5.7.2.1 注入验证器

    LocalValidatorFactoryBean implements both javax.validation.Validator and org.springframework.validation.Validator. 您可以将这两个接口之一的引用注入到需要调用验证逻辑的 bean 中。

    如果您更喜欢直接使用 JSR-303 API,请注入对 javax.validation.Validator 的引用:

    // JSR-303 Validator
    import javax.validation.Validator;
    
    @Service
    public class MyService {
    
        @Autowired
        private Validator validator;
    
    }
    

    如果您的 bean 需要 Spring Validation API,则注入对 org.springframework.validation.Validator 的引用:

    // Spring Validator
    import org.springframework.validation.Validator;
    
    @Service
    public class MyService {
    
        @Autowired
        private Validator validator;
    
    }
    

    这是一个解释清楚的例子 Using JSR 303 with "classic" Spring Validators (enter the SpringValidatorAdapter)

    这个链接很有帮助。包装 javax.validation.Validator org.springframework.validation.beanvalidation.SpringValidatorAdapter 帮助始终如一地处理错误。您可以将此添加为答案吗 我可以接受它

    Spring doc here

    【讨论】:

    • 我认为 Spring Boot 也为你提供了 Validator bean(不是你自己很难做到)。
    • 这是否意味着spring验证器只能在@Service类中使用来触发验证?我已经在网上做了足够的阅读,所以看到了这样的例子,但我仍然不明白如何在 POJO 中做到这一点,因此提出了这个问题。我也知道如何触发自定义验证器的验证,但不知道如何触发 POJO 中的默认验证。如果可能的话,你能在我所拥有的范围内提供具体的答案吗?
    • 您可以将 Validator 注入任何 Spring bean。这里有一个老例子 nonrepeatable.blogspot.com/2010/04/… 。如果您仍有疑虑,请在此处评论
    • 这个链接很有帮助。在 org.springframework.validation.beanvalidation.SpringValidatorAdapter 中包装 javax.validation.Validator 有助于始终如一地处理错误。您能否将其添加为答案,以便我接受。
    • 我已经编辑了这个答案以包含链接,所以你可以接受这个,很高兴我能提供帮助
    猜你喜欢
    • 1970-01-01
    • 2017-11-29
    • 1970-01-01
    • 1970-01-01
    • 2019-10-17
    • 1970-01-01
    • 2021-11-28
    • 2011-04-21
    相关资源
    最近更新 更多