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

Валидация формы с помощью HTTP-декодера

В этом примере мы создадим асинхронный сервлет, который добавляет контакты в список, разбирает запросы и обрабатывает валидацию формы с помощью Decoder. Рассмотрим этот пример как краткое представление паттерна MVC:

  • Для моделирования представления Contact мы создадим обычный Java-класс с полями (имя, возраст, адрес), конструктором и доступными элементами к полям.
  • Для упрощения примера мы будем использовать ArrayList для хранения объектов Contact . Для этого используется интерфейс ContactDAO и его реализация.
  • Для построения представления мы будем использовать один HTML-файл, скомпилированный с помощью шаблонизатора Mustache.
  • В качестве контроллера будет использоваться AsyncServlet . Мы также добавим RoutingServlet для маршрутизации конкретного запроса к определенной конечной точке.
  • Декодер предоставляет вам инструменты для разбора запросов.
See full example on GitHub

Здесь мы рассмотрим только класс HttpDecoderExample с AsyncServlet , так как он содержит специфические для ActiveJ возможности.

Создание HttpDecoderExample класса

Создадим класс HttpDecoderExample , который расширяет HttpServerLauncher. Расширяя HttpServerLauncher , мы позаботимся о жизненном цикле сервера и управлении сервисами. Далее мы предоставляем два пользовательских анализатора на основе HTTP Декодер- ADDRESS_DECODER и CONTACT_DECODER - которые будут использоваться для проверки.

public final class HttpDecoderExample extends HttpServerLauncher {  private static final String SEPARATOR = "-";
  private static final Decoder<Address> ADDRESS_DECODER = Decoder.      of(Address::new,          ofPost("title", "")              .validate(param -> !param.isEmpty(), "Title cannot be empty")      );
  private static final Decoder<Contact> CONTACT_DECODER = Decoder.of(Contact::new,      ofPost("name")          .validate(name -> !name.isEmpty(), "Name cannot be empty"),      ofPost("age")          .map(Mapper.ofEx(Integer::valueOf, "Cannot decode age"))          .validate(age -> age >= 18, "Age must not be less than 18"),      ADDRESS_DECODER.withId("contact-address")  );
note

Если вы хотите узнать больше об интеграции шаблонизаторов, посмотрите этот пример.

Также необходимо создать applyTemplate(Mustache mustache, Map<String, Object> scopes) метод для заполнения предоставленного шаблона Mustache заданными данными:

private static ByteBuf applyTemplate(Mustache mustache, Map<String, Object> scopes) {  ByteBufWriter writer = new ByteBufWriter();  mustache.execute(writer, scopes);  return writer.getBuf();}

Далее, давайте предоставим фабричный метод ContactDAOImpl :

@ProvidesContactDAO dao() {  return new ContactDAOImpl();}

Теперь у нас есть все необходимое для создания AsyncServlet для обработки запросов:

@ProvidesAsyncServlet mainServlet(ContactDAO contactDAO) {  Mustache contactListView = new DefaultMustacheFactory().compile("static/contactList.html");  return RoutingServlet.create()      .map("/", request ->          HttpResponse.ok200()              .withBody(applyTemplate(contactListView, mapOf("contacts", contactDAO.list()))))      .map(POST, "/add", request -> request.loadBody()          .map($ -> {            //[START REGION_3]            Either<Contact, DecodeErrors> decodedUser = CONTACT_DECODER.decode(request);            //[END REGION_3]            if (decodedUser.isLeft()) {              contactDAO.add(decodedUser.getLeft());            }            Map<String, Object> scopes = mapOf("contacts", contactDAO.list());            if (decodedUser.isRight()) {              //noinspection ConstantConditions - is 'right', hence not 'null'              scopes.put("errors", decodedUser.getRight().toMap(SEPARATOR));            }            return HttpResponse.ok200()                .withBody(applyTemplate(contactListView, scopes));          }));}
  • Здесь мы предоставляем AsyncServlet, который получает HttpRequests от клиентов, создает HttpResponses в зависимости от маршрута и отправляет их.
  • Внутри RoutingServlet определены два маршрутных пути. Первый из них соответствует запросам к корневому маршруту "/" - он просто отображает список контактов. Второй, "/add" - это метод HTTP POST , который добавляет или отклоняет добавление новых пользователей. Мы будем обрабатывать этот запрос с помощью вышеупомянутого HTTP Decoder , используя decode(request) метод:
Either<Contact, DecodeErrors> decodedUser = CONTACT_DECODER.decode(request);
  • Либо представляет собой значение двух возможных типов данных (Contact, DecodeErrors). Либо либо Левый(Контакт), либо Правый(DecodeErrors). Для того чтобы определить, был ли разбор успешным или нет, мы проверяем его значение с помощью методов isLeft() и isRight() . Наконец, напишите метод main() , который запустит наше приложение:
public static void main(String[] args) throws Exception {  Launcher launcher = new HttpDecoderExample();  launcher.launch(args);}

Запуск приложения

Если вы хотите запустить пример, вам нужно клонировать ActiveJ с GitHub и импортировать его как проект Maven. Посмотрите ветку v5.4.3. Перед запуском примера выполните сборку проекта (Ctrl F9 для IntelliJ IDEA).

Затем откройте класс HttpDecoderExample , который находится по адресу activej/examples/tutorials/decoder и запустите его метод main() . Откройте ваш любимый браузер и перейдите по адресу localhost:8080.