Валидиране на боб в Джърси

1. Общ преглед

В този урок ще разгледаме Bean Validation, използвайки Джърси с отворен код.

Както вече видяхме в предишни статии, Джърси е рамка с отворен код за разработване на RESTful Web Services. Можем да получим повече подробности за Джърси в нашето въведение за това как да създадем API с Джърси и Пролет.

2. Валидиране на боб в Джърси

Проверката е процес на проверка, че някои данни се подчиняват на едно или повече предварително дефинирани ограничения . Това, разбира се, е много често срещан случай на употреба в повечето приложения.

Рамката за проверка на Bean Java (JSR-380) се превърна в де факто стандарт за обработка на този вид операции в Java. За да обобщите основите на Java Bean Validation, моля, вижте нашия предишен урок.

Джърси съдържа модул за удължаване в подкрепа на Bean Validation . За да използваме тази възможност в нашето приложение, първо трябва да я конфигурираме. В следващия раздел ще видим как да конфигурираме нашето приложение.

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

Сега нека надградим върху простия пример за Fruit API от отличната статия за поддръжка на Jersey MVC.

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

Първо, нека добавим зависимостта Bean Validation към нашия pom.xml :

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

Можем да получим най-новата версия от Maven Central.

3.2. Конфигуриране на сървъра

В Джърси обикновено регистрираме функцията за разширение, която искаме да използваме в нашия клас за конфигуриране на персонализирани ресурси.

За разширението за валидиране на боб обаче не е необходимо да се прави тази регистрация. За щастие, това е едно от малкото разширения, които рамката на Джърси се регистрира автоматично.

И накрая, за да изпратим грешки при проверка на клиента , ще добавим свойство на сървъра към нашата персонализирана конфигурация на ресурса :

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

4. Валидиране на JAX-RS ресурсни методи

В този раздел ще обясним два различни начина за валидиране на входните параметри с помощта на анотации на ограничения:

  • Използване на вградени ограничения за API за проверка на Bean
  • Създаване на персонализирано ограничение и валидатор

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, която вече е част от API за проверка на Bean.

Това налага просто не нулево ограничение на нашите параметри на формата. В случай, че един от параметрите е нулев , съобщението, декларирано в анотацията, ще бъде върнато .

Естествено, ще покажем това с единичен тест:

@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. Дефиниране на анотация на персонализирано ограничение

Понякога трябва да наложим по-сложни ограничения. Можем да направим това, като дефинираме нашата собствена анотация.

Използвайки нашия прост пример за API за плодове, нека си представим, че трябва да потвърдим, че всички плодове имат валиден сериен номер:

@PUT @Path("/update") @Consumes("application/x-www-form-urlencoded") public void updateFruit(@SerialNumber @FormParam("serial") String 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. Resource Validation

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

In the next section, we’ll explain two different ways of validating resource classes using this annotation:

  • First, Request resource validation
  • Second, Response resource validation

Let's begin by adding the @Min annotation to our Fruit object:

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

5.1. Request Resource Validation

First of all, we'll enable validation using @Valid in our FruitResource class:

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

In the above example, if we try to create a fruit with a weight less than 10 we will get a validation error.

5.2. Response Resource Validation

Likewise, in the next example, we'll see how to validate a response resource:

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

Note, how we use the same @Valid annotation. But this time we use it at the resource method level to be sure the response is valid.

6. Custom Exception Handler

In this last part, we'll briefly look at how to create a custom exception handler. This is useful when we want to return a custom response if we violate a particular constraint.

Let's begin by defining our 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(); } }

First of all, we define a custom exception mapping provider. In order to do this, we implement the ExceptionMapper interface using a ConstraintViolationException.

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

Also, in this simple example, we iterate through all the violations and append each property and message to be sent back in the response.

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

Finally, we add an endpoint to return an invalid Fruit to show the exception handler in action:

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

7. Conclusion

За да обобщим, в този урок проучихме разширението на API за проверка на Jersey Bean.

Първо, започнахме с представянето как API за проверка на зърна може да се използва в Джърси. Също така разгледахме как да конфигурираме примерно уеб приложение.

Накрая разгледахме няколко начина за извършване на проверка с Джърси и как да напишем персонализиран манипулатор на изключения.

Както винаги, пълният изходен код на статията е достъпен в GitHub.