В этом уроке мы изучим формы, которые меняют данные. И с клиентской, и с серверной стороны они обычно устроены сложнее, чем поисковые формы. Для уверенной работы с ними нужно разбираться:
- Как работают соответствующие HTML-теги
- Как отправляются формы по HTTP
- Как происходит обработка на стороне сервера
- Как работает валидация и вывод ошибок
Все это мы изучим в ближайшее время.
Вывод формы
За вывод формы и ее обработку должны отвечать два разных маршрута — а значит, еще и два обработчика. Рассмотрим примеры маршрутов для создания нового пользователя:
- GET /users/build — страница с формой, которую заполняет пользователь. Эта форма отправляет POST-запрос на адрес /users, указанный в атрибуте
action
- POST /users — маршрут, обрабатывающий данные формы
Для нашей задачи мы будем использовать класс пользователя. Он может выглядеть так:
@Getter
@Setter
public final class User {
private Long id;
private String name;
private String email;
private String password;
public User(String name, String email, String password) {
this.name = name;
this.email = email;
this.password = password;
}
}
Добавим обработчик маршрута /users/build, который выводит форму добавления пользователя:
app.get("/users/build", ctx -> {
ctx.render("users/build.jte");
});
Создадим форму в файле src/main/jte/users/build.jte:
<form action="/users" method="post">
<div>
<label>
Имя
<input type="text" name="name" />
</label>
</div>
<div>
<label>
Email
<input type="email" required name="email" />
</label>
</div>
<div>
<label>
Пароль
<input type="password" required name="password" />
</label>
</div>
<div>
<label>
Подтверждение пароля
<input type="password" required name="passwordConfirmation" />
</label>
</div>
<input type="submit" value="Зарегистрировать" />
</form>
Запустим сервер и убедимся, что форма выводится по адресу /users/build. Если ее заполнить и попытаться отправить, то браузер сформирует следующий HTTP-запрос:
# Данные взяты для примера
POST /users HTTP/1.1
Host: localhost
Content-type: application/x-www-form-urlencoded
Content-length: 76
name=Mike&email=example@test.com&password=qwerty&passwordConfirmation=qwerty
В примере выше мы видели работу с HTTP, но на практике все устроено немного по-другому. Не стоит передавать электронную почту, пароль и другие чувствительные данные по обычному HTTP-протоколу, потому что так данные легко перехватить и использовать не по назначению.
Именно поэтому современные сайты обычно работают по протоколу HTTPS, который передает данные в зашифрованном виде.
Обработка данных
Форма готова, теперь можно реализовывать ее обработчик. Обработчик формы работает так:
- Извлекает параметры формы через метод
ctx.formParam()
- Формирует объект пользователя на основе этих параметров
- Сохраняет объект пользователя в базу данных
- Выполняет редирект — например, на страницу пользователей
Код обработчика выглядит так:
app.post("/users", ctx -> {
var name = ctx.formParam("name");
var email = ctx.formParam("email");
var password = ctx.formParam("password");
var passwordConfirmation = ctx.formParam("passwordConfirmation");
var user = new User(name, email, password);
UserRepository.save(user);
ctx.redirect("/users");
});
Примерно так выглядит большинство обработчиков форм. Они могут быть сложнее, но общая структура остается похожей: мы извлекаем данные, создаем нужные объекты на основе них и вносим изменения в базу. В конце мы выполняем редирект или формируем необходимый ответ клиенту.
Нормализация данных
Представим, что при регистрации пользователь написал свою почту в произвольном регистре — MYname@example.com вместо myname@example.com. По спецификации, адреса электронной почты не зависят от регистра. Другими словами, если в двух адресах совпадают буквы, то они считаются одним и тем же адресом.
Проблема в том, что с точки зрения кода и большинства баз данных мы сравниваем строки с учетом регистра. В итоге наш проект ждут два сюрприза:
- Пользователь сможет зарегистрировать несколько аккаунтов на один и тот же адрес почты, просто вводя адрес в разном регистре
- Пользователь не сможет зайти на сайт, если введет почту не в том регистре, который использовал при регистрации
Чтобы этого не происходило, введенные пользователями данные проходят нормализацию. В случае почты мы приводим ее к нижнему регистру:
// Это касается только адреса электронной почты
var name = ctx.formParam("name");
var email = ctx.formParam("email").toLowerCase();
var password = ctx.formParam("password");
Пользователь может не только ошибиться в регистре, но и случайно поставить пробелы в конце или в начале строк — например, из-за копирования. Иногда мы не можем на это влиять. Например, пользователь может специально добавить пробельный символ в начале и конце пароля.
А вот для имени и адреса почты такое недопустимо. Поэтому лучше обрезать концевые пробелы для надежности:
var name = ctx.formParam("name").trim();
var email = ctx.formParam("email").trim().toLowerCase();
var password = ctx.formParam("password");
Самостоятельная работа
- Выполните на своем компьютере все шаги из урока
- Добавьте в приложение форму и обработчики для добавления нового пользователя. Не забудьте сделать нормализацию электронной почты пользователя
- Измените код приложения так, чтобы пользователи в обработчиках подтягивались из репозитория
- Запустите приложение и попробуйте добавлять новых пользователей
- Проверьте, происходит ли редирект на страницу просмотра всех пользователей
- Проверьте, отображается ли новый пользователь в списке пользователей
- Сделайте то же самое для курсов
- Залейте изменения на GitHub
Остались вопросы? Задайте их в разделе «Обсуждение»
Вам ответят команда поддержки Хекслета или другие студенты
Для полного доступа к курсу нужен базовый план
Базовый план откроет полный доступ ко всем курсам, упражнениям и урокам Хекслета, проектам и пожизненный доступ к теории пройденных уроков. Подписку можно отменить в любой момент.