【问题标题】:Spring Data Rest Validation ConfusionSpring Data Rest 验证混乱
【发布时间】:2016-05-21 07:44:35
【问题描述】:

寻求有关正确处理验证错误的 Spring 数据休息验证方面的帮助:

我对这里有关 spring-data-rest 验证的文档感到非常困惑:http://docs.spring.io/spring-data/rest/docs/current/reference/html/#validation

我正在尝试正确处理尝试保存新公司实体的 POST 调用的验证

我得到了这个实体:

@Entity
public class Company implements Serializable {

@Id
@GeneratedValue
private Long id;

@NotNull
private String name;

private String address;

private String city;

private String country;

private String email;

private String phoneNumber;

@OneToMany(cascade = CascadeType.ALL, mappedBy = "company")
private Set<Owner> owners = new HashSet<>();

public Company() {
    super();
}

...

还有这个 RestResource 道

import org.springframework.data.repository.PagingAndSortingRepository;
import org.springframework.data.rest.core.annotation.RestResource;

import com.domain.Company;

@RestResource
public interface CompanyDao extends PagingAndSortingRepository<Company,   Long> {


}

对 api/Companies 的 POST 请求:

{
  "address" : "One Microsoft Way",
  "city" : "Redmond",
  "country" : "USA",
  "email" : "info@microsoft.com",
  "phoneNumber" : "(425) 703-6214"

}

当我发出一个带有空名称的 POST 时,我得到以下带有 httpcode 500 的休息响应

{"timestamp":1455131008472,"status":500,"error":"Internal Server Error","exception":"javax.validation.ConstraintViolationException","message":"类 [com. domain.Company] 组 [javax.validation.groups.Default, ]\n 约束违规列表:[\n\tConstraintViolationImpl{interpolatedMessage='可能不为空', propertyPath=name, rootBeanClass=class com.domain .Company, messageTemplate='{javax.validation.constraints.NotNull.message}'}\n]","path":"/api/companies/"}

我尝试创建以下 bean,但它似乎从来没有做任何事情:

@Component(value="beforeCreateCompanyValidator")
public class BeforeCreateCompanyValidator implements Validator{

@Override
public boolean supports(Class<?> clazz) {
    return Company.class.isAssignableFrom(clazz);
}

@Override
public void validate(Object arg0, Errors arg1) {
    System.out.println("xxxxxxxx");


}

}

即使它确实有效,它如何帮助我使用正确的 http 代码和可理解的 json 响应来开发更好的错误响应?

很迷茫

使用 1.3.2.RELEASE

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>1.3.2.RELEASE</version>
    <relativePath/> <!-- lookup parent from repository -->
</parent>

【问题讨论】:

    标签: spring-boot spring-data-rest spring-rest


    【解决方案1】:

    @Mathias 似乎以下内容足以检查 jsr 303 注释并自动返回带有漂亮消息的 400 的 http 代码(我什至不需要 BeforeCreateCompanyValidator 或 BeforeSaveCompanyValidator 类):

    @Configuration
    public class RestValidationConfiguration extends RepositoryRestConfigurerAdapter{
    
    @Bean
    @Primary
    /**
     * Create a validator to use in bean validation - primary to be able to autowire without qualifier
     */
    Validator validator() {
        return new LocalValidatorFactoryBean();
    }
    
    
    @Override
    public void configureValidatingRepositoryEventListener(ValidatingRepositoryEventListener validatingListener) {
        Validator validator = validator();
        //bean validation always before save and create
        validatingListener.addValidator("beforeCreate", validator);
        validatingListener.addValidator("beforeSave", validator);
    }
    

    }

    400 响应:

    {
        "errors": [{
            "entity": "Company",
            "message": "may not be null",
            "invalidValue": "null",
            "property": "name"
        }, {
            "entity": "Company",
            "message": "may not be null",
            "invalidValue": "null",
            "property": "address"
        }]
    }
    

    【讨论】:

    • 是的 - 如果你只有带注释的约束就足够了 - 如果你需要更复杂的验证,你会引入一个自定义验证器 - 我只是想展示它是如何工作的。
    • 感谢您的回答,这已经困扰了我将近一个小时。我从没想过会做一个 LocalValidatorFactoryBean
    【解决方案2】:

    我认为您的问题是 bean 验证发生得太晚了 - 它是在 JPA 级别上完成的,然后再持久化。我发现 - 与 spring mvc 不同 - spring-data-rest 在调用控制器方法时不进行 bean 验证。为此,您将需要一些额外的配置。

    您希望 spring-data-rest 验证您的 bean - 这将为您提供很好的错误消息响应和正确的 http 返回代码。

    我在 spring-data-rest 中配置了我的验证,如下所示:

    @Configuration
    public class MySpringDataRestValidationConfiguration extends RepositoryRestConfigurerAdapter {
    
        @Bean
        @Primary
        /**
         * Create a validator to use in bean validation - primary to be able to autowire without qualifier
         */
        Validator validator() {
            return new LocalValidatorFactoryBean();
        }
    
        @Bean
        //the bean name starting with beforeCreate will result into registering the validator before insert
        public BeforeCreateCompanyValidator beforeCreateCompanyValidator() {
            return new BeforeCreateCompanyValidator();
        }
    
        @Override
        public void configureValidatingRepositoryEventListener(ValidatingRepositoryEventListener validatingListener) {
            Validator validator = validator();
            //bean validation always before save and create
            validatingListener.addValidator("beforeCreate", validator);
            validatingListener.addValidator("beforeSave", validator);
        }
    }
    

    当 bean 验证和/或我的自定义验证器发现错误时,我会收到一个 400 - bad request,其有效负载如下:

        Status = 400
        Error message = null
        Headers = {Content-Type=[application/hal+json]}
        Content type = application/hal+json
       Body = {
         "errors" : [ {
         "entity" : "siteWithAdminUser",
         "message" : "may not be null",
         "invalidValue" : "null",
         "property" : "adminUser"
         } ]
       }
    

    【讨论】:

    • 超级有魅力,顺便说一句,无论如何自动检查jsr 303注释而不是我在这里做的“手动”东西:@Override public void validate(Object object, Errors errors) {公司公司=(公司)对象; if(StringUtils.isEmpty(company.getName())){ errors.rejectValue("name", "name.required", "Name field is missing"); } }
    • 我认为如果你在类路径上有 hibernate-validator 你应该也可以使用这些约束注释——例如org.hibernate.validator.constraints.NotEmpty
    • 我想知道为什么您的解决方案仍然适用于创建和更新(因为我没有添加 BeforeSaveCompanyValidator..).. 所以我开始尝试......似乎我没有甚至需要一个 BeforeCreateCompanyValidator 或 BeforeUpdateCompanyValidator,因为 LocalValidatorFactoryBean 配置 bean 可以处理这一切。我将在上面的原始帖子中发布您课程的新版本。它似乎工作得很好,但仍然有点困惑......
    【解决方案3】:

    @Mathias 和@1977 的回答足以应付常规的Spring Data REST 电话。但是,当您需要使用 @RequestBody@Valid 编写自定义 @RepositoryRestControllers 时,JSR-303 注释对我不起作用。

    因此,作为答案的补充,如果自定义@RepositoryRestControllers 带有@RequestBody@Valid 注释,我添加了以下@ControllerAdvice

    /**
     * Workaround class for making JSR-303 annotation validation work for controller method parameters.
     * Check the issue <a href="https://jira.spring.io/browse/DATAREST-593">DATAREST-593</a>
     */
    
        @ControllerAdvice
        public class RequestBodyValidationProcessor extends RequestBodyAdviceAdapter {
    
            private final Validator validator;
    
            public RequestBodyValidationProcessor(@Autowired @Qualifier("mvcValidator") final Validator validator) {
                this.validator = validator;
            }
    
            @Override
            public boolean supports(final MethodParameter methodParameter, final Type targetType, final Class<? extends
                    HttpMessageConverter<?>> converterType) {
                final Annotation[] parameterAnnotations = methodParameter.getParameterAnnotations();
                for (final Annotation annotation : parameterAnnotations) {
                    if (annotation.annotationType().equals(Valid.class)) {
                        return true;
                    }
                }
    
                return false;
            }
    
            @Override
            public Object afterBodyRead(final Object body, final HttpInputMessage inputMessage, final MethodParameter
                    parameter, final Type targetType, final Class<? extends HttpMessageConverter<?>> converterType) {
                final Object obj = super.afterBodyRead(body, inputMessage, parameter, targetType, converterType);
                final BindingResult bindingResult = new BeanPropertyBindingResult(obj, obj.getClass().getCanonicalName());
                validator.validate(obj, bindingResult);
                if (bindingResult.hasErrors()) {
                    throw new RuntimeBindException(bindingResult);
                }
    
                return obj;
            }
        }
    

    【讨论】:

    • 不错!谢谢你:)
    猜你喜欢
    • 2016-03-02
    • 2014-08-10
    • 2020-05-13
    • 2018-08-22
    • 2017-03-06
    • 1970-01-01
    • 2020-02-09
    • 1970-01-01
    相关资源
    最近更新 更多