Перейти к основному содержимому

Проверка бобов в Джерси

· 6 мин. чтения

1. Обзор

В этом уроке мы рассмотрим валидацию бинов с использованием фреймворка с открытым исходным кодом Jersey .

Как мы уже видели в предыдущих статьях, Jersey — это платформа с открытым исходным кодом для разработки веб-служб RESTful. Мы можем получить более подробную информацию о Джерси в нашем введении о том, как создать API с помощью Джерси и Spring.

2. Проверка бинов в Джерси

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

Инфраструктура Java Bean Validation (JSR-380) стала стандартом де-факто для обработки такого рода операций в Java. Чтобы повторить основы проверки Java Bean, обратитесь к нашему предыдущему руководству .

Jersey содержит модуль расширения для поддержки Bean Validation . Чтобы использовать эту возможность в нашем приложении, нам сначала нужно ее настроить. В следующем разделе мы увидим, как настроить наше приложение.

3. Настройка приложения

Теперь давайте возьмем простой пример Fruit API из превосходной статьи о поддержке MVC в Джерси .

3.1. Зависимости Maven

Прежде всего, давайте добавим зависимость Bean Validation в наш pom.xml :

<dependency>
<groupId>org.glassfish.jersey.ext</groupId>
<artifactId>jersey-bean-validation</artifactId>
<version>2.27</version>
</dependency>

Мы можем получить последнюю версию от Maven Central .

3.2. Настройка сервера

В Джерси мы обычно регистрируем функцию расширения, которую хотим использовать, в нашем пользовательском классе конфигурации ресурсов.

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

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

public ViewApplicationConfig() {
packages("com.foreach.jersey.server");
property(ServerProperties.BV_SEND_ERROR_IN_RESPONSE, true);
}

4. Проверка методов ресурсов JAX-RS

В этом разделе мы объясним два разных способа проверки входных параметров с помощью аннотаций ограничений:

  • Использование встроенных ограничений Bean Validation API
  • Создание пользовательского ограничения и валидатора

4.1. Использование встроенных аннотаций ограничений

Начнем с рассмотрения встроенных аннотаций ограничений:

@POST
@Path("/create")
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
public void createFruit(
@NotNull(message = "Fruit name must not be null") @FormParam("name") String name,
@NotNull(message = "Fruit colour must not be null") @FormParam("colour") String colour) {

Fruit fruit = new Fruit(name, colour);
SimpleStorageService.storeFruit(fruit);
}

В этом примере мы создаем новый Fruit , используя два параметра формы: имя и цвет . Мы используем аннотацию @NotNull , которая уже является частью Bean Validation API.

Это накладывает простое ненулевое ограничение на параметры формы. Если один из параметров имеет значение null , будет возвращено сообщение, объявленное в аннотации .

Естественно, мы продемонстрируем это с помощью модульного теста:

@Test
public void givenCreateFruit_whenFormContainsNullParam_thenResponseCodeIsBadRequest() {
Form form = new Form();
form.param("name", "apple");
form.param("colour", null);
Response response = target("fruit/create").request(MediaType.APPLICATION_FORM_URLENCODED)
.post(Entity.form(form));

assertEquals("Http Response should be 400 ", 400, response.getStatus());
assertThat(response.readEntity(String.class), containsString("Fruit colour must not be null"));
}

В приведенном выше примере мы используем класс поддержки JerseyTest для тестирования нашего фруктового ресурса . Мы отправляем POST-запрос с нулевым цветом и проверяем, что ответ содержит ожидаемое сообщение.

Список встроенных ограничений проверки можно найти в документации .

4.2. Определение аннотации пользовательского ограничения

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

Используя наш простой пример Fruit API, давайте представим, что нам нужно проверить, что все фрукты имеют действительный серийный номер:

@PUT
@Path("/update")
@Consumes("application/x-www-form-urlencoded")
public void updateFruit(@SerialNumber @FormParam("serial") String serial) {
//...
}

В этом примере параметр serial должен удовлетворять ограничениям, определенным @SerialNumber , которые мы определим далее.

Сначала мы определим аннотацию ограничения:

@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = { SerialNumber.Validator.class })
public @interface SerialNumber {

String message()

default "Fruit serial number is not valid";

Class<?>[] groups() default {};

Class<? extends Payload>[] payload() default {};
}

Далее мы определим класс валидатора SerialNumber.Validator :

public class Validator implements ConstraintValidator<SerialNumber, String> {
@Override
public void initialize(SerialNumber serial) {
}

@Override
public boolean isValid(String serial,
ConstraintValidatorContext constraintValidatorContext) {

String serialNumRegex = "^\\d{3}-\\d{3}-\\d{4}$";
return Pattern.matches(serialNumRegex, serial);
}
}

Ключевым моментом здесь является то, что класс Validator должен реализовать ConstraintValidator , где T — это тип значения, которое мы хотим проверить , в нашем случае это String .

Наконец, мы реализуем нашу пользовательскую логику проверки в методе isValid .

5. Проверка ресурсов

Кроме того, Bean Validation API также позволяет нам проверять объекты с помощью аннотации @Valid .

В следующем разделе мы объясним два разных способа проверки классов ресурсов с помощью этой аннотации:

  • Во-первых, запросить проверку ресурса
  • Во-вторых, проверка ресурса ответа

Давайте начнем с добавления аннотации @Min к нашему объекту Fruit :

@XmlRootElement
public class Fruit {

@Min(value = 10, message = "Fruit weight must be 10 or greater")
private Integer weight;
//...
}

5.1. Запросить проверку ресурсов

Прежде всего, мы включим проверку с помощью @Valid в нашем классе FruitResource :

@POST
@Path("/create")
@Consumes("application/json")
public void createFruit(@Valid Fruit fruit) {
SimpleStorageService.storeFruit(fruit);
}

В приведенном выше примере, если мы попытаемся создать фрукт весом менее 10, мы получим ошибку проверки.

5.2. Проверка ресурсов ответа

Аналогично, в следующем примере мы увидим, как проверить ресурс ответа:

@GET
@Valid
@Produces("application/json")
@Path("/search/{name}")
public Fruit findFruitByName(@PathParam("name") String name) {
return SimpleStorageService.findByName(name);
}

Обратите внимание, как мы используем ту же аннотацию @Valid . Но на этот раз мы используем его на уровне метода ресурса, чтобы убедиться, что ответ действителен.

6. Пользовательский обработчик исключений

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

Давайте начнем с определения нашего FruitExceptionMapper :

public class FruitExceptionMapper implements ExceptionMapper<ConstraintViolationException> {

@Override
public Response toResponse(ConstraintViolationException exception) {
return Response.status(Response.Status.BAD_REQUEST)
.entity(prepareMessage(exception))
.type("text/plain")
.build();
}

private String prepareMessage(ConstraintViolationException exception) {
StringBuilder message = new StringBuilder();
for (ConstraintViolation<?> cv : exception.getConstraintViolations()) {
message.append(cv.getPropertyPath() + " " + cv.getMessage() + "\n");
}
return message.toString();
}
}

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

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

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

Затем, чтобы использовать наш собственный преобразователь исключений, нам нужно зарегистрировать нашего провайдера :

@Override
protected Application configure() {
ViewApplicationConfig config = new ViewApplicationConfig();
config.register(FruitExceptionMapper.class);
return config;
}

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

@GET
@Produces(MediaType.TEXT_HTML)
@Path("/exception")
@Valid
public Fruit exception() {
Fruit fruit = new Fruit();
fruit.setName("a");
fruit.setColour("b");
return fruit;
}

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

Подводя итог, в этом руководстве мы изучили расширение API проверки фасоли Джерси.

Во-первых, мы начали с представления того, как можно использовать Bean Validation API в Джерси. Кроме того, мы рассмотрели, как настроить пример веб-приложения.

Наконец, мы рассмотрели несколько способов выполнения проверки с помощью Джерси и способы написания собственного обработчика исключений.

Как всегда, полный исходный код статьи доступен на GitHub .