В этом уроке мы изучим формы, которые меняют данные. И с клиентской, и с серверной стороны они обычно устроены сложнее, чем поисковые формы. Для уверенной работы с ними нужно разбираться:
- Как работают соответствующие HTML-теги
- Как отправляются формы по HTTP
- Как происходит обработка на стороне сервера
- Как работает валидация и вывод ошибок
Все это мы изучим в ближайшее время.
Вывод формы
За вывод формы и ее обработку должны отвечать два разных маршрута — а значит, еще и два обработчика. Рассмотрим примеры маршрутов для создания нового пользователя:
- GET /users/new — страница с формой, которую заполняет пользователь. Эта форма отправляет POST-запрос на адрес /users, указанный в атрибуте
action
- POST /users — маршрут, обрабатывающий данные формы
Добавим обработчик маршрута /users/new, который выводит форму добавления пользователя:
app.get('/users/new', (req, res) => {
res.view('src/views/users/new')
})
Создадим форму в файле src/views/users/new.pug
html
body
form(action = '/users', method = 'post')
div
label Имя:
input(type = 'text', name = 'name')
div
label Email:
input(type = 'email', name = 'email', required = true)
div
label Пароль:
input(type = 'password', name = 'password', required = true)
div
label Подтверждение пароля:
input(type = 'password', name = 'passwordConfirmation', required = true)
input(type = 'submit', value = 'Зарегистрировать')
Запустите сервер и убедитесь, что форма выводится по адресу /users/new. Если ее заполнить и попытаться отправить, то браузер сформирует следующий 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, который передает данные в зашифрованном виде.
Обработка данных
Форма готова, теперь можно реализовывать ее обработчик. Обработчик формы работает так:
- Извлекает параметры формы
- Формирует объект пользователя на основе этих параметров
- Сохраняет пользователя в базу данных
- Выполняет редирект — например, на страницу пользователей
Все данные приходят в виде строки. Удобнее всего добавить плагин, который будет помогать парсить тело запроса. Для этого устанавливаем нужный пакет:
npm i @fastify/formbody
И подключаем плагин в приложение:
import fastify from 'fastify'
import formbody from '@fastify/formbody'
const app = fastify()
await app.register(formbody)
Код обработчика выглядит так:
app.post('/users', (req, res) => {
const user = {
name: req.body.name,
email: req.body.email,
password: req.body.password,
}
state.users.push(user)
res.redirect('/users')
})
Примерно так выглядит большинство обработчиков форм. Они могут быть сложнее, но общая структура остается похожей: мы извлекаем данные, создаем нужные объекты на основе них и вносим изменения в базу. В конце мы выполняем редирект или формируем необходимый ответ клиенту.
Нормализация данных
Представим, что при регистрации пользователь написал свою почту в произвольном регистре — MYname@example.com вместо myname@example.com. По спецификации, адреса электронной почты не зависят от регистра. Другими словами, если в двух адресах совпадают буквы, то они считаются одним и тем же адресом.
Проблема в том, что с точки зрения кода и большинства баз данных мы сравниваем строки с учетом регистра. В итоге наш проект ждут два сюрприза:
- Пользователь сможет зарегистрировать несколько аккаунтов на один и тот же адрес почты, просто вводя адрес в разном регистре
- Пользователь не сможет зайти на сайт, если введет почту не в том регистре, который использовал при регистрации
Чтобы этого не происходило, введенные пользователями данные проходят нормализацию. В случае почты мы приводим ее к нижнему регистру:
// Касается только емейла
const user = {
name: req.body.name,
email: req.body.email.toLowerCase(),
password: req.body.password,
}
Пользователь может не только ошибиться в регистре, но и случайно поставить пробелы в конце или в начале строк — например, из-за копирования. Иногда мы не можем на это влиять. Например, пользователь может специально добавить пробельный символ в начале и конце пароля.
А вот для имени и адреса почты такое недопустимо. Поэтому лучше обрезать концевые пробелы для надежности:
const user = {
name: req.body.name.trim(),
email: req.body.email.trim().toLowerCase(),
password: req.body.password,
}
Самостоятельная работа
- Выполните у себя на компьютере все шаги из урока. Добавьте в приложение форму и обработчики для добавления нового пользователя. Не забудьте сделать нормализацию email пользователя.
- Измените код приложения так, чтобы пользователи в обработчиках подтягивались из репозитория
- Запустите приложение и попробуйте добавлять новых пользователей. Проверьте, что происходит редирект на страницу просмотра всех пользователей и в списке пользователей отображается новый пользователь
- Сделайте то же самое для курсов и залейте изменения на GitHub
Для полного доступа к курсу нужен базовый план
Базовый план откроет полный доступ ко всем курсам, упражнениям и урокам Хекслета, проектам и пожизненный доступ к теории пройденных уроков. Подписку можно отменить в любой момент.