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

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

1. обзор

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

Как мы уже видели в предыдущих статьях,Jersey is an open source framework for developing RESTful Web Services. Мы можем получить более подробную информацию о Джерси во введении о том, какcreate an API with Jersey and Spring.

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

Validation is the process of verifying that some data obeys one or more pre-defined constraints. Это, конечно, очень распространенный вариант использования в большинстве приложений.

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

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

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

Теперь давайте рассмотрим простой пример Fruit API из отличной статьиJersey MVC Support.

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

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


    org.glassfish.jersey.ext
    jersey-bean-validation
    2.27

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

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

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

Однако для расширения проверки бина нет необходимости делать эту регистрацию. Fortunately, this is one of the few extensions that the Jersey framework registers automatically.с

Наконец, чтобы отправить клиенту ошибки проверкиwe’ll add a server property to our a custom resource configuration:

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

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

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

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

  • Создание пользовательского ограничения и валидатора

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, используя два параметра формы,name иcolour. Мы используем аннотацию@NotNull, которая уже является частью Bean Validation API.

Это накладывает простое ненулевое ограничение на наши параметры формы. In case one of the parameters is null, the message declared within the annotation will be returned.

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

@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"));
}

В приведенном выше примереwe use the JerseyTest support class to test our fruit resource. Мы отправляем запрос POST с нулевым значениемcolour и проверяем, содержит ли ответ ожидаемое сообщение.

Список встроенных ограничений проверки можно найти вat the docs.

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

Иногда нам нужно наложить более сложные ограничения. We can do this by defining our own custom annotation.с

Используя наш простой пример 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[] payload() default {};
}

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

public class Validator implements ConstraintValidator {
    @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);
    }
}

The key point here is the Validator class must implement ConstraintValidator where T is the type of value we want to validate, in our case a String.

Finally, we then implement our custom validation logic in the isValid method.

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

Furthermore, the Bean Validation API also allows us to validate objects using the @Valid annotation.

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

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

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

Начнем с добавления аннотации@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);
}

В приведенном выше примереif we try to create a fruit with a weight less than 10 we will get a validation error.

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

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

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

Обратите внимание, как мы используем ту же аннотацию@Valid. But this time we use it at the resource method level to be sure the response is valid.

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

В этой последней части мы кратко рассмотрим, как создать собственный обработчик исключений. This is useful when we want to return a custom response if we violate a particular constraint.с

Начнем с определения нашегоFruitExceptionMapper:

public class FruitExceptionMapper implements ExceptionMapper {

    @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.

Hence, we’ll see that when this exception is thrown the toResponse method of our custom exception mapper instance will be invoked.

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

Next, in order to use our custom exception mapper we need to register our provider:

@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 проверки бобов Jersey Bean.

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

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

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