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