Spring Data Rest Validation Confusion

时间:2016-02-10 19:15:13

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

寻找有关正确处理验证错误的Spring数据休息验证的一些帮助:

我对有关弹簧数据休息验证的文档感到困惑: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 dao

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 /公司的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”:“内部服务器错误”,“异常”:“javax.validation.ConstraintViolationException”,“message”:“类的验证失败[com。 domain.Company]在组的持续时间[javax.validation.groups.Default,] \ n违反约束的列表:[\ n \ t \ nConstraintViolationImpl {interpolatedMessage ='可能不为null',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>

3 个答案:

答案 0 :(得分:8)

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

}

响应404,{“errors”:[{“entity”:“Company”,“message”:“may not null”,“invalidValue”:“null”,“property”:“name”},{ “entity”:“Company”,“message”:“may not null”,“invalidValue”:“null”,“property”:“address”}]}

答案 1 :(得分:4)

我认为你的问题是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错误的请求,其中包含这样的有效负载:

    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"
     } ]
   }

答案 2 :(得分:2)

@Mathias和@ 1977的答案对于常规Spring Data REST来说已经足够了。但是,如果您需要使用@RepositoryRestController@RequestBody编写自定义@Valid,则JSR-303注释对我无效。

因此,作为答案的补充,如果自定义@RepositoryRestController带有@RequestBody@Valid注释,我已添加以下@ControllerAdvice:< / p>

/**
 * 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;
        }
    }