Валидация формы с помощью HTTP-декодера
В этом примере мы создадим асинхронный сервлет, который добавляет контакты в список, разбирает запросы и обрабатывает валидацию формы с помощью Decoder
. Рассмотрим этот пример как краткое представление паттерна MVC:
- Для моделирования представления
Contact
мы создадим обычный Java-класс с полями (имя, возраст, адрес), конструктором и доступными элементами к полям. - Для упрощения примера мы будем использовать
ArrayList
для хранения объектовContact
. Для этого используется интерфейсContactDAO
и его реализация. - Для построения представления мы будем использовать один HTML-файл, скомпилированный с помощью шаблонизатора Mustache.
- В качестве контроллера будет использоваться
AsyncServlet
. Мы также добавимRoutingServlet
для маршрутизации конкретного запроса к определенной конечной точке. - Декодер предоставляет вам инструменты для разбора запросов.
Здесь мы рассмотрим только класс 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"
- это метод HTTPPOST
, который добавляет или отклоняет добавление новых пользователей. Мы будем обрабатывать этот запрос с помощью вышеупомянутого HTTPDecoder
, используя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.