Рубрики
Без рубрики

Руководство по валидаторам Spring Data REST

Краткое и практическое руководство по валидаторам Spring Data REST

Автор оригинала: baeldung.

1. Обзор

Эта статья посвящена основному введению в валидаторы Spring Data REST. Если вам нужно сначала ознакомиться с основами весеннего отдыха данных, обязательно посетите эту статью, чтобы освежить основы.

Проще говоря, с Spring Data REST мы можем просто добавить новую запись в базу данных через REST API, но нам, конечно, также нужно убедиться, что данные действительны, прежде чем фактически сохранять их.

Эта статья продолжает существующую статью, и мы будем повторно использовать существующий проект, который мы там создали.

И, если вы хотите сначала начать работу с Spring Data REST – вот хороший способ начать работу:

2. Использование Валидаторов

Начиная с Spring 3, фреймворк имеет интерфейс Validator , который можно использовать для проверки объектов.

2.1. Мотивация

В предыдущей статье мы определили нашу сущность, имеющую два свойства – имя и адрес электронной почты .

И поэтому, чтобы создать новый ресурс, нам просто нужно запустить:

curl -i -X POST -H "Content-Type:application/json" -d 
  '{ "name" : "Test", "email" : "[email protected]" }' 
  http://localhost:8080/users

Этот запрос POST сохранит предоставленный объект JSON в нашей базе данных, и операция вернет:

{
  "name" : "Test",
  "email" : "[email protected]",
  "_links" : {
    "self" : {
        "href" : "http://localhost:8080/users/1"
    },
    "websiteUser" : {
        "href" : "http://localhost:8080/users/1"
    }
  }
}

Ожидался положительный результат, поскольку мы предоставили достоверные данные. Но что произойдет, если мы удалим свойство name или просто установим значение в пустую строку ?

Чтобы проверить первый сценарий, мы запустим модифицированную команду из предыдущего, где мы установим пустую строку в качестве значения для свойства name :

curl -i -X POST -H "Content-Type:application/json" -d 
  '{ "name" : "", "email" : "Baggins" }' http://localhost:8080/users

С помощью этой команды мы получим следующий ответ:

{
  "name" : "",
  "email" : "Baggins",
  "_links" : {
    "self" : {
        "href" : "http://localhost:8080/users/1"
    },
    "websiteUser" : {
        "href" : "http://localhost:8080/users/1"
    }
  }
}

Для второго сценария мы удалим свойство name из запроса:

curl -i -X POST -H "Content-Type:application/json" -d 
  '{ "email" : "Baggins" }' http://localhost:8080/users

Для этой команды мы получим такой ответ:

{
  "name" : null,
  "email" : "Baggins",
  "_links" : {
    "self" : {
        "href" : "http://localhost:8080/users/2"
    },
    "websiteUser" : {
        "href" : "http://localhost:8080/users/2"
    }
  }
}

Как мы видим, оба запроса были в порядке, и мы можем подтвердить это с помощью кода состояния 201 и ссылки API на наш объект .

Такое поведение неприемлемо, так как мы хотим избежать вставки частичных данных в базу данных.

2.2. Весенние события отдыха данных

Во время каждого вызова API Spring Data REST экспортер Spring Data REST генерирует различные события, перечисленные здесь:

  • Перед Созданием События
  • После Создания События
  • Перед событием сохранения
  • AfterSaveEvent
  • Перед событием сохранения ссылок
  • AfterLinkSaveEvent
  • Перед завершением События
  • После события Удаления

Поскольку все события обрабатываются аналогичным образом, мы покажем только, как обрабатывать перед созданием события , которое генерируется перед сохранением нового объекта в базе данных.

2.3. Определение валидатора

Чтобы создать наш собственный валидатор, нам нужно реализовать org.springframework.validation.Валидатор интерфейс с поддерживает и проверяет методы.

Поддерживает проверяет, поддерживает ли валидатор предоставленные запросы, в то время как метод validate проверяет предоставленные данные в запросах.

Давайте определим класс Website UserValidator :

public class WebsiteUserValidator implements Validator {

    @Override
    public boolean supports(Class clazz) {
        return WebsiteUser.class.equals(clazz);
    }

    @Override
    public void validate(Object obj, Errors errors) {
        WebsiteUser user = (WebsiteUser) obj;
        if (checkInputString(user.getName())) {
            errors.rejectValue("name", "name.empty");
        }
   
        if (checkInputString(user.getEmail())) {
            errors.rejectValue("email", "email.empty");
        }
    }

    private boolean checkInputString(String input) {
        return (input == null || input.trim().length() == 0);
    }
}

Ошибки объект-это специальный класс, предназначенный для хранения всех ошибок, указанных в методе validate . Позже в этой статье мы покажем, как вы можете использовать предоставленные сообщения, содержащиеся в Errors object.
Чтобы добавить новое сообщение об ошибке, мы должны вызвать errors.rejectValue(имя Поля, сообщение об ошибке)
.

После того, как мы определили валидатор, нам нужно сопоставить его с определенным событием, которое генерируется после принятия запроса.

Например, в нашем случае перед созданием события генерируется, потому что мы хотим вставить новый объект в нашу базу данных. Но поскольку мы хотим проверить объект в запросе, нам нужно сначала определить наш валидатор.

Это можно сделать тремя способами:

  • Добавьте Компонент аннотацию с именем ” перед созданием UserValidator веб-сайта “. Spring Boot распознает префикс перед созданием , который определяет событие, которое мы хотим поймать, а также распознает WebsiteUser class из Component name.

  • Создайте Bean в контексте приложения с помощью @Bean аннотации:

  • Ручная регистрация:
    • В этом случае вам не нужны никакие аннотации на Website UserValidator class.

2.4. Ошибка обнаружения событий

На данный момент в Spring Data REST существует ошибка , которая влияет на обнаружение событий.

Если мы вызовем POST-запрос, который генерирует событие beforeCreate , ваше приложение не вызовет валидатор, потому что событие не будет обнаружено из-за этой ошибки.

Простым решением этой проблемы является вставка всех событий в Spring Data REST ValidatingRepositoryEventListener class:

@Configuration
public class ValidatorEventRegister implements InitializingBean {

    @Autowired
    ValidatingRepositoryEventListener validatingRepositoryEventListener;

    @Autowired
    private Map validators;

    @Override
    public void afterPropertiesSet() throws Exception {
        List events = Arrays.asList("beforeCreate");
        for (Map.Entry entry : validators.entrySet()) {
            events.stream()
              .filter(p -> entry.getKey().startsWith(p))
              .findFirst()
              .ifPresent(
                p -> validatingRepositoryEventListener
               .addValidator(p, entry.getValue()));
        }
    }
}

3. Тестирование

В Разделе 2.1. мы показали, что без валидатора мы можем добавлять объекты без свойства name в нашу базу данных, что нежелательно, поскольку мы не проверяем целостность данных.

Если мы хотим добавить тот же объект без имени свойства, но с предоставленным валидатором, мы получим эту ошибку:

curl -i -X POST -H "Content-Type:application/json" -d 
  '{ "email" : "[email protected]" }' http://localhost:8080/users
{  
   "timestamp":1472510818701,
   "status":406,
   "error":"Not Acceptable",
   "exception":"org.springframework.data.rest.core.
    RepositoryConstraintViolationException",
   "message":"Validation failed",
   "path":"/users"
}

Как мы видим, отсутствующие данные из запроса были обнаружены, и объект не был сохранен в базе данных. Наш запрос был возвращен с 500 HTTP-кодом и сообщением о внутренней ошибке.

В сообщении об ошибке ничего не говорится о проблеме в нашем запросе. Если мы хотим сделать его более информативным, нам придется изменить объект ответа.

В статье “Обработка исключений в Spring” мы показали , как обрабатывать исключения, генерируемые фреймворком, так что на данный момент это определенно хорошее чтение.

Поскольку наше приложение генерирует исключение нарушения ограничений репозитория исключение, мы создадим обработчик для этого конкретного исключения, который изменит ответное сообщение.

Это наш Rest ResponseEntity Обработчик исключений r класс:

@ControllerAdvice
public class RestResponseEntityExceptionHandler extends
  ResponseEntityExceptionHandler {

    @ExceptionHandler({ RepositoryConstraintViolationException.class })
    public ResponseEntity handleAccessDeniedException(
      Exception ex, WebRequest request) {
          RepositoryConstraintViolationException nevEx = 
            (RepositoryConstraintViolationException) ex;

          String errors = nevEx.getErrors().getAllErrors().stream()
            .map(p -> p.toString()).collect(Collectors.joining("\n"));
          
          return new ResponseEntity(errors, new HttpHeaders(),
            HttpStatus.PARTIAL_CONTENT);
    }
}

С помощью этого пользовательского обработчика наш возвращаемый объект будет содержать информацию обо всех обнаруженных ошибках.

4. Заключение

В этой статье мы показали, что валидаторы необходимы для каждого API Spring Data REST, который обеспечивает дополнительный уровень безопасности для вставки данных.

Мы также проиллюстрировали, как просто создать новый валидатор с аннотациями.

Как всегда, код для этого приложения можно найти в проекте GitHub .