«1. Обзор

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

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

2. Проверка компонентов в Джерси

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

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

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

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. Настройка сервера

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

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

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

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

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

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

    Использование встроенных ограничений 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.

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

    Во-первых, проверка ресурсов запроса. Во-вторых, проверка ресурсов ответа. Фруктовый объект:

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

@XmlRootElement
public class Fruit {

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

«