Spring Проверка достоверности данных

Поиск помощи с Spring валидацией данных для правильной обработки ошибок проверки:

Я настолько смущен документами относительно spring -data-rest validation здесь: 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> {


}

POST Запрос на api/Компании:

{
  "address" : "One Microsoft Way",
  "city" : "Redmond",
  "country" : "USA",
  "email" : "[email protected]",
  "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,]\nList нарушений ограничений: [\n\tConstraintViolationImpl {интерполяцияMessage = 'не может быть null", propertyPath = name, rootBeanclass= class com.domain.Company, messageTemplate = '{javax.validation.constraints.NotNull.message}'}\n] "," путь ":" /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>

Ответ 1


@Mathias, кажется, следующего достаточно для проверки аннотаций jsr 303 и автоматического возврата http-кода 400 с красивыми сообщениями (мне даже не нужны классы 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"
    }]
}

Ответ 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 - плохой запрос с полезной нагрузкой, как это:

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

Ответ 3

Ответы @Mathias и @1977 достаточно для регулярных вызовов Spring Data REST. Однако в тех случаях, когда вам нужно писать пользовательские @RepositoryRestController с помощью @RequestBody и @Valid, аннотации JSR-303 не работали для меня.

Итак, в качестве дополнения к ответу, в случае пользовательской @RepositoryRestController с аннотацией @RequestBody и @Valid я добавил следующее @ControllerAdvice:

/**
 * Workaround class for making JSR-303 annotation validation work for controller method parameters.
 * Check the issue <a href="#" onclick="location.href='https://jira.spring.io/browse/DATAREST-593'; return false;">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;
        }
    }