Spring in Action Covers Spring 5-1--11 — страница 10 из 63

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

Одним из способов выполнения проверки формы является заполнение методов processDesign() и processOrder() множеством блоков if/then, проверяя каждое поле, чтобы убедиться, что оно соответствует соответствующим правилам проверки. Но это было бы громоздко и трудно читать и отлаживать.

К счастью, Spring поддерживает API проверки компонентов Java (также известный как JSR-303; https://jcp.org/en/jsr/detail?id=303). Это упрощает объявление правил проверки в отличие от явного написания логики объявления в коде приложения. И с Spring Boot, вам не нужно делать ничего особенного, чтобы добавить библиотеки проверки в свой проект, потому что API проверки и реализация Hibernate API проверки автоматически добавляются в проект в качестве временных зависимостей веб-стартера Spring Boot.

Чтобы применить проверку в Spring MVC, необходимо:

Объявите правила проверки классам, которые должны быть проверены: в частности, класс Taco.

Указать, что проверка должна быть выполнена в методах контроллера, которые требуют проверки: в частности, метод processDesign в DesignTacoController() и в OrderController метод processOrder().

Измените представления формы для отображения ошибок проверки.

API проверки предлагает несколько аннотаций, которые можно поместить в свойства объектов домена для объявления правил проверки. Реализация Hibernate API проверки добавляет еще больше аннотаций проверки. Давайте посмотрим, как вы можете применить некоторые из этих аннотаций для проверки представленного Taco или Order.

2.3.1 Определение правил проверки

Для класса Taco необходимо убедиться, что свойство name не пустое и не null и что в списке выбранных ингредиентов есть хотя бы один элемент. В следующем списке показан обновленный класс Taco, который использует @NotNull и @Size для объявления этих правил проверки.

Листинг 2.10 Добавленные проверки в класс Taco

package tacos;


import java.util.List;


import javax.validation.constraints.NotNull;


import javax.validation.constraints.Size; import lombok.Data;



@Data


public class Taco {


 @NotNull


 @Size(min=5, message="Name must be at least 5 characters long")


 private String name;



 @Size(min=1, message="You must choose at least 1 ingredient")


 private List ingredients;


}

Вы заметите, что в дополнение к требованию, чтобы свойство name не было null, вы объявляете, что оно должно иметь значение длиной не менее 5 символов.

Когда дело доходит до объявления проверки представленных заказов тако, необходимо применить аннотации к классу Order.  Для свойств адреса необходимо только убедиться, что пользователь не оставляет пустыми ни одно из полей. Для этого вы будете использовать аннотацию Hibernate Validator @NotBlank.

Однако проверка полей платежей немного более экзотична.  Необходимо не только убедиться, что свойство ccNumber не пусто, но и что оно содержит значение, которое может быть допустимым номером кредитной карты. Свойство ccExpiration должно соответствовать формату MM/YY (двухзначные месяц и год). И свойство ccCVV должно быть трехзначным. Для достижения такого рода проверки необходимо использовать несколько других аннотаций Java Bean Validation API и заимствовать аннотацию проверки из коллекции аннотаций Hibernate Validator.  В следующем листинге показаны изменения, необходимые для проверки класса Order.

Листинг 2.11 Проверка полей заказа

package tacos;

import javax.validation.constraints.Digits;


import javax.validation.constraints.Pattern;


import org.hibernate.validator.constraints.CreditCardNumber;


import javax.validation.constraints.NotBlank;


import lombok.Data;



@Data


public class Order {


 @NotBlank(message="Name is required")


 private String name;



 @NotBlank(message="Street is required")


 private String street;

 @NotBlank(message="City is required")


 private String city;



 @NotBlank(message="State is required")


 private String state;



 @NotBlank(message="Zip code is required")


 private String zip;



 @CreditCardNumber(message="Not a valid credit card number")


 private String ccNumber;



 @Pattern(regexp="^(0[1-9]|1[0-2])([\\/])([1-9][0-9])$", message="Must be formatted MM/YY")


 private String ccExpiration;



 @Digits(integer=3, fraction=0, message="Invalid CVV")


  private String ccCVV;


}

Как видите, свойство ccNumber  снабжено аннотацией @CreditCardNumber.  Эта аннотация объявляет, что значение свойства должно быть допустимым номером кредитной карты, который проходит проверку алгоритма Luhn (https://en.wikipedia.org/wiki/Luhn_algorithm). Это предотвращает ошибки пользователей и преднамеренно неверные данные, но не гарантирует, что номер кредитной карты фактически принадлежит учетной записи или что номер карты пригоден для оплаты.

К сожалению, нет готовой аннотации для проверки формата MM/YY свойства ccExpiration. Я применил аннотацию @Pattern, предоставив ей регулярное выражение, которое гарантирует, что значение свойства придерживается желаемого формата. Если вам интересно, как расшифровать регулярное выражение, я рекомендую вам ознакомиться со многими онлайн-руководствами по регулярным выражениям, в том числе http://www.regular-expressions.info/. Синтаксис регулярных выражений является темным искусством и, безусловно, выходит за рамки этой книги.

Наконец, свойство ccCVV аннотируется @Digits, чтобы убедиться, что значение содержит ровно три цифры.

Все аннотации проверки содержат атрибут message, определяющий сообщение, которое будет отображаться пользователю, если введенная им информация не соответствует требованиям объявленных правил проверки.

2.3.2 Выполнение проверок

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

Чтобы проверить отправленное Taco, необходимо добавить аннотацию Java Bean Validation API’s @Valid

к аргументу Taco метода processDesign() у DesignTacoController.

Листинг 2.12 Проверка POST-запроса Taco

@PostMapping


public String processDesign(@Valid Taco design, Errors errors) {


 if (errors.hasErrors()) {


   return "design";


 }


 // Save the taco design...


 // We'll do this in chapter 3


 log.info("Processing design: " + design);


 return "redirect:/orders/current";


}

@Valid аннотация говорит Spring MVC, чтобы выполнить проверку на представленном объекте Taco после того, как он привязан к представленным данным формы и перед вызовом метода processDesign(). Если есть какие-либо ошибки проверки, сведения об этих ошибках будут записаны в объект Errors, который передается в processDesign(). Первые несколько строк processDesign() проверяют объект Errors, через его метод hasErrors (), есть ли какие-либо ошибки проверки. Если есть, метод завершается без обработки Taco и возвращает имя представления "design" так, чтобы форма была перерисована.

Для выполнения проверки переданных объектов Order, аналогичные изменения также требуются в методе processOrder() в OrderController.

Листинг 2.13 Проверка -POST-запроса Order

@PostMapping


public String processOrder(@Valid Order order, Errors errors) {


 if (errors.hasErrors()) {


   return "orderForm";


 }


 log.info("Order submitted: " + order);


 return "redirect:/";


}

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

Но как пользователь узнает, какие ошибки требуют исправления? Если вы не отобразите ошибки в форме, пользователю останется только гадать о том, как успешно отправить форму.

2.3.3 Отображение ошибок проверки

Thymeleaf предлагает удобный доступ к объекту Errors через свойство fields и с его атрибутом th:errors. Например, чтобы отобразить ошибки проверки в поле номер кредитной карты, можно добавить элемент , который использует эти ссылки на ошибки в шаблоне формы заказа следующим образом.

Листинг 2.14 Отображение ошибок проверки




 th:if="${#fields.hasErrors('ccNumber')}"


 th:errors="*{ccNumber}">CC Num Error

Помимо атрибута класса, который можно использовать для стилизации ошибки, чтобы привлечь внимание пользователя, элемент использует атрибут th:if, чтобы решить, отображать ли . Метод hasErrors() свойства fields проверяет наличие ошибок в поле ccNumber. Если да, то будет отрисован.

Атрибут th: errors ссылается на поле ccNumber и, если для этого поля имеются ошибки, он заменит содержимое заполнителя элемента сообщением о результате проверки.

Если вы добавите похожие теги для других полей, вы увидите форму, которая выглядит как на рисунке 2.4, когда вы предоставляете недопустимую информацию. Ошибки указывают, что поля name, city и ZIP code оставлены пустыми и что все поля платежа не соответствуют критериям проверки.


Рисунок 2.4 Отображение ошибок проверки в форме заказа