Spring boot Spring数据Rest验证混乱

Spring boot Spring数据Rest验证混乱,spring-boot,spring-data-rest,spring-rest,Spring Boot,Spring Data Rest,Spring Rest,正在寻找有关正确处理验证错误的Spring data rest验证帮助: 我对这里有关spring数据rest验证的文档感到非常困惑: 我正在尝试正确地处理试图拯救新公司实体的POST调用的验证 我得到了这个实体: @Entity public class Company implements Serializable { @Id @GeneratedValue private Long id; @NotNull private String name; private String ad

正在寻找有关正确处理验证错误的Spring data rest验证帮助:

我对这里有关spring数据rest验证的文档感到非常困惑:

我正在尝试正确地处理试图拯救新公司实体的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();
}
当我发出一个带有空名称的帖子时,我会得到以下带有httpcode 500的rest响应

{“timestamp”:1455131008472,“status”:500,“error”:“Internal Server error”,“exception”:“javax.validation.ConstraintViolationException”,“message”:“在组[javax.validation.groups.Default]的持续时间内,类[com.domain.Company]的验证失败,]\n约束冲突列表:[\n\t约束冲突:[{interpolatedMessage='may not null',propertyPath=name,rootBeanClass=class com.domain.Company,messageTemplate='{javax.validation.constraints.NotNull.message}'}\n],“path”:“/api/companys/”}

我尝试创建以下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");


}

}
组件(value=“beforeCreateCompanyValidator”) 在CreateCompanyValidator实现验证器之前的公共类{ @凌驾 公共布尔支持(类clazz){ 返回公司.class.isAssignableFrom(clazz); } @凌驾 公共无效验证(对象arg0,错误arg1){ 系统输出打印项次(“xxxxxxxx”); } } 即使它确实有效,它如何帮助我用正确的http代码和可理解的json响应开发更好的错误响应呢

如此困惑

使用1.3.2.1版本

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

org.springframework.boot
spring启动程序父级
1.3.2.1发布

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

您希望SpringDataREST验证您的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);
    }
}
    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"
     } ]
   }
当bean验证和/或我的自定义验证器发现错误时,我会收到一个400错误的请求,负载如下:

@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);
    }
}
    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"
     } ]
   }

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

您希望SpringDataREST验证您的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);
    }
}
    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"
     } ]
   }
当bean验证和/或我的自定义验证器发现错误时,我会收到一个400错误的请求,负载如下:

@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);
    }
}
    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"
     } ]
   }

@马蒂亚斯 以下内容似乎足以检查JSR303注释,并自动返回400的http代码和漂亮的消息(我甚至不需要BeforeCreateCompanyValidator或BeforeSaveCompanyValidator类):

}

400答复:

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

@马蒂亚斯 以下内容似乎足以检查JSR303注释,并自动返回400的http代码和漂亮的消息(我甚至不需要BeforeCreateCompanyValidator或BeforeSaveCompanyValidator类):

}

400答复:

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

@Mathias和@1977的答案对于常规
Spring数据REST
调用来说已经足够了。但是,当您需要使用
@RequestBody
@Valid
编写自定义
@RepositoryRestController
时,JSR-303注释对我不起作用

因此,作为对答案的补充,对于带有
@RequestBody
@Valid
注释的自定义
@RepositoryRestController
s,我添加了以下
@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;
        }
    }
/**
*使JSR-303注释验证适用于控制器方法参数的解决方案类。
*检查问题
*/
@控制器建议
公共类RequestBodyValidationProcessor扩展了RequestBodyAdviceAdapter{
私人最终验证器;
public RequestBodyValidationProcessor(@Autowired@Qualifier(“mvcValidator”)最终验证器验证器){
this.validator=验证程序;
}
@凌驾
公共布尔支持(final MethodParameter MethodParameter、final类型targetType、final类>converterType){
最终注释[]parameterAnnotations=methodParameter.getParameterAnnotations();
对于(最终注释:参数注释){
if(annotation.annotationType().equals(Valid.class)){
返回true;
}
}
返回false;
}
@凌驾
公共对象afterBodyRead(最终对象体、最终HttpInputMessage inputMessage、最终方法参数
参数,最终类型targetType,最终类>转换器类型){
final Object obj=super.afterBodyRead(body,inputMessage,参数,targetType,converterType);
final BindingResult BindingResult=新的BeanPropertyBindingResult(obj,obj.getClass().getCanonicalName());
validator.validate(obj,bindingResult);
if(bindingResult.hasErrors()){
抛出新的RuntimeBindException(bindingResult);
}
返回obj;
}
}

对于常规的
Spring数据REST
调用,@Mathias和@1977给出的答案就足够了。但是,当您需要使用
@RequestBody
@Valid
编写自定义
@RepositoryRestController
时,JSR-303注释对我不起作用

因此,作为对答案的补充,如果自定义
@RepositoryRestController
s带有
@RequestBody
@Valid
ann