Если вы создаете
Как правило, процесс обработки формы включает в себя:
отображение пустой
пользователь, отправляющий форму с данными в POSTзапросе
проверка как на клиенте, так и на сервере
повторное отображение формы, заполненной экранированными данными и сообщениями об ошибках, если они недействительны
перенаправление пользователя или отображение сообщения об успешном выполнении после обработки данных.
Обработка данных формы также связана с дополнительными соображениями безопасности.
Мы рассмотрим все это и объясним, как создавать их с помощью Node.js и Express — самого популярного
Контактная форма с электронной почтой и сообщением с ошибками проверки
Как всегда, полный код можно найти в нашем репозитории GitHub.
Настраивать
Убедитесь, что у вас установлена последняя версия Node.js. node -vдолжен вернуться 8.9.0или выше.
Загрузите исходный код отсюда с помощью Git:
git clone -b starter https://github.com/sitepoint-editors/node-forms.git
cd
npm install
npm start
Примечание. Репозиторий имеет две ветки starterи master. Ветка starterсодержит минимальную настройку, необходимую для выполнения этой статьи. Ветка masterсодержит полную рабочую демоверсию (ссылка выше).
Там не так много кода. Это просто простая настройка Express с шаблонами EJS и обработчиками ошибок:
// server.js
const path = require ('path’) ;
const express = require ('express’) ;
const layout = require ('
const routes = require ('. /routes’) ;
const app = express () ;
app.set ('views’, path.join (__dirname, 'views’));
app.set ('view engine’, 'ejs’) ;
const middlewares = [
layout (),
express.static (path.join (__dirname, 'public’)),
];
app.use (middlewares) ;
app.use ('/', routes) ;
app.use ((req, res, next) => {
res.status (404).send («Sorry can’t find that!») ;
}) ;
app.use ((err, req, res, next) => {
console.error (err.stack) ;
res.status (500).send ('Something broke!') ;
}) ;
app.listen (3000, () => {
console.log ('App running at http: //localhost:3000') ;
}) ;
Корневой URL -адрес /просто отображает index.ejsпредставление:
// routes.js
const express = require ('express’) ;
const router = express.Router () ;
router.get ('/', (req, res) => {
res.render ('index’) ;
}) ;
module.exports = router;
Отображение формы
Когда люди делают запрос GET /contact, мы хотим отобразить новое представление contact.ejs:
// routes.js
router.get ('/contact’, (req, res) => {
res.render ('contact’) ;
}) ;
Контактная форма позволит им отправить нам сообщение и адрес электронной почты:
Send us a message
Посмотрите, как это выглядит на http: //localhost:3000/contact.
Отправка формы
Чтобы получать значения POST в Express, вам сначала нужно включить
// server.js
const bodyParser = require ('
const middlewares = [
//...
bodyParser.urlencoded ({ extended: true }),
];
Для форм принято отправлять данные POST обратно на тот же
Сначала рассмотрим недействительную отправку. Если они недействительны, нам нужно вернуть отправленные значения в представление (чтобы пользователям не нужно было вводить их повторно) вместе с любыми сообщениями об ошибках, которые мы хотим отобразить:
router.get ('/contact’, (req, res) => {
res.render ('contact’, {
data: {},
errors: {}
}) ;
}) ;
router.post ('/contact’, (req, res) => {
res.render ('contact’, {
data: req.body, // { message, email }
errors: {
message: {
msg: 'A message is required’
},
email: {
msg: 'That email doesn"t look right’
}
}
}) ;
}) ;
Если есть
отображать ошибки в верхней части формы
установите входные значения на то, что было отправлено на сервер
отображать встроенные ошибки под входными данными
добавить
<% if (Object.keys (errors).length === 0) {%>
Send us a message
<% } else {%>
Oops, please correct the following:
<% Object.values (errors).forEach (error => {%>
<% })%>
- <%= error.msg%>
<% }%>
Отправьте форму http: //localhost:3000/contact, чтобы увидеть это в действии. Это все, что нам нужно на стороне просмотра.
Проверка и санитарная обработка
Существует удобное промежуточное программное обеспечение под названием
Проверка
С предоставленными валидаторами мы можем легко проверить, что сообщение и действительный адрес электронной почты были предоставлены:
// routes.js
const { check, validationResult, matchedData } = require ('
router.post ('/contact’, [
check ('message’)
.isLength ({ min: 1 })
.withMessage ('Message is required’),
check ('email’)
.isEmail ()
.withMessage ('That email doesn"t look right’)
], (req, res) => {
const errors = validationResult (req) ;
res.render ('contact’, {
data: req.body,
errors: errors.mapped ()
}) ;
}) ;
Санитарная обработка
С помощью предоставленных дезинфицирующих средств мы можем обрезать пробелы в начале и в конце значений и нормализовать адрес электронной почты в соответствии с шаблоном. Это может помочь удалить повторяющиеся контакты, созданные с помощью немного разных входных данных. Например, ' Mark@gmail.com’и 'mark@gmail.com
router.post ('/contact’, [
check ('message’)
.isLength ({ min: 1 })
.withMessage ('Message is required’)
.trim (),
check ('email’)
.isEmail ()
.withMessage ('That email doesn"t look right’)
.bail ()
.trim ()
.normalizeEmail ()
], (req, res) => {
const errors = validationResult (req) ;
res.render ('contact’, {
data: req.body,
errors: errors.mapped ()
}) ;
const data = matchedData (req) ;
console.log ('Sanitized:', data) ;
}) ;
Функция matchedDataвозвращает вывод дезинфицирующих средств на наш ввод.
Также обратите внимание на использование нами метода залога, который прекращает выполнение проверок, если
Действительная форма
Если есть ошибки, нам нужно перерендерить представление. Если нет, нам нужно сделать
HTTP не имеет состояния, поэтому вы не можете перенаправить на другую страницу и передавать сообщения без помощи файла cookie сеанса, чтобы сохранить это сообщение между
Есть три промежуточных программного обеспечения, которые нам нужно включить, чтобы подключить это:
// server.js
const cookieParser = require ('
const session = require ('
const flash = require ('
const middlewares = [
//...
cookieParser (),
session ({
secret: '
key: '
resave: false,
saveUninitialized: false,
cookie: { maxAge: 60000 }
}),
flash (),
];
Промежуточное
// routes
router.post ('/contact’, [
// validation...
], (req, res) => {
const errors = validationResult (req) ;
if (! errors.isEmpty ()) {
return res.render ('contact’, {
data: req.body,
errors: errors.mapped ()
}) ;
}
const data = matchedData (req) ;
console.log ('Sanitized: ', data) ;
// Homework: send sanitized data in an email or persist to a db
req.flash ('success’, 'Thanks for the message! I"ll be in touch:)') ;
res.redirect ('/') ;
}) ;
Промежуточное
<% if (messages.success) {%>
<% }%>
Working With Forms in Node.js
Теперь вы должны быть перенаправлены в indexпредставление и увидеть сообщение об успехе, когда форма будет отправлена с действительными данными. Ура! Теперь мы можем развернуть это в рабочей среде и получать сообщения от принца Нигерии.
Отправка электронной почты с помощью узла
Вы, возможно, заметили, что фактическая отправка почты оставлена читателю в качестве домашнего задания. Это не так сложно, как может показаться, и может быть выполнено с помощью пакета Nodemailer. Вы можете найти простые инструкции по настройке здесь или более подробное руководство здесь.
Соображения безопасности
Если вы работаете с формами и сеансами в Интернете, вам необходимо знать об общих уязвимостях в
TLS через HTTPS
Всегда используйте шифрование TLS при https: //работе с формами, чтобы отправляемые данные шифровались при отправке через Интернет. Если вы отправляете данные формы через http: //, они отправляются в виде обычного текста и могут быть видны любому, кто подслушивает эти пакеты, когда они путешествуют по сети.
Если вы хотите узнать больше о работе с SSL/TLS в Node.js, ознакомьтесь с этой статьей.
Наденьте свой шлем
Есть аккуратное маленькое промежуточное ПО под названием шлем, которое добавляет некоторую защиту от заголовков HTTP. Лучше всего включать прямо в верхнюю часть вашего промежуточного программного обеспечения, и это очень легко включить:
// server.js
const helmet = require ('helmet’) ;
middlewares = [
helmet (),
//...
];
Подделка межсайтовых запросов (CSRF)
Вы можете защитить себя от подделки межсайтовых запросов, создав уникальный токен, когда пользователю предоставляется форма, а затем проверив этот токен перед обработкой данных POST. Здесь также есть промежуточное программное обеспечение, которое поможет вам:
// routes.js
const csrf = require ('csurf’) ;
const csrfProtection = csrf ({ cookie: true }) ;
В
// routes.js
router.get ('/contact’, csrfProtection, (req, res) => {
res.render ('contact’, {
data: {},
errors: {},
csrfToken: req.csrfToken ()
}) ;
}) ;
А также в ответе на ошибки проверки:
router.post ('/contact’, csrfProtection, [
// validations...
], (req, res) => {
const errors = validationResult (req) ;
if (! errors.isEmpty ()) {
return res.render ('contact’, {
data: req.body,
errors: errors.mapped (),
csrfToken: req.csrfToken ()
}) ;
}
//...
}) ;
Затем нам просто нужно включить токен в скрытый ввод:
Это все, что требуется.
Нам не нужно изменять наш обработчик
Вы можете проверить это самостоятельно, отредактировав или удалив токен из формы с помощью инструментов разработчика вашего браузера и отправив его.
Межсайтовый скриптинг (XSS)
Вы должны проявлять осторожность при отображении данных, отправленных пользователем, в представлении HTML, так как это может открыть вам доступ к межсайтовым сценариям (XSS). Все языки шаблонов предоставляют разные методы вывода значений. EJS <%= value%>выводит экранированное значение HTML, чтобы защитить вас от XSS, тогда как <%- value%>выводит необработанную строку.
Всегда используйте экранированный вывод <%= value%>при работе со значениями, отправленными пользователем. Используйте необработанные выходные данные только в том случае, если вы уверены, что это безопасно.
Загрузка файлов
Загрузка файлов в формах HTML — это особый случай, требующий тип кодировки «multipart/
Вам потребуется дополнительное ПО промежуточного слоя для обработки многокомпонентных загрузок. Там есть пакет Express с именем multer, который мы будем использовать здесь:
// routes.js
const multer = require ('multer’) ;
const upload = multer ({ storage: multer.memoryStorage () }) ;
router.post ('/contact’, upload.single ('photo’), csrfProtection, [
// validation...
], (req, res) => {
// error handling...
if (req.file) {
console.log ('Uploaded: ', req.file) ;
// Homework: Upload file to S3
}
req.flash ('success’, 'Thanks for the message! I’ll be in touch:)') ;
res.redirect ('/') ;
}) ;
Этот код указывает multerзагрузить файл в поле «фото» в память и выставляет Fileобъект в req.file, который мы можем проверить или обработать дальше.
Последнее, что нам нужно, это добавить enctypeатрибут и наш входной файл:
Попробуйте загрузить файл. Вы должны увидеть Fileобъекты, зарегистрированные в консоли.
Заполнение входных файлов
В случае ошибок проверки мы не можем повторно заполнить файловые входы, как мы сделали для текстовых входов (это угроза безопасности). Общий подход к решению этой проблемы включает следующие шаги:
загрузка файла во временное место на сервере
показ эскиза и имени прикрепленного файла
добавление JavaScript в форму, чтобы люди могли удалить выбранный файл или загрузить новый
перемещение файла в постоянное место, когда все в порядке.
Загрузка файлов с помощью узла
Наконец, вы заметите, что читателю предоставлена возможность реализовать фактическую функциональность загрузки. Это не так сложно, как может показаться, и может быть выполнено с помощью различных пакетов, таких как Formidable или
Спасибо за чтение
Надеюсь, вам было интересно узнать о
отображение пустой формы в ответ на
обработка отправленных данных POST
отображение списка ошибок, встроенных ошибок и отправленных данных
проверка отправленных данных с помощью валидаторов
очистка отправленных данных с помощью дезинфицирующих средств
передача сообщений через перенаправления с
защитить себя от атак, таких как CSRF и XSS
обработка загрузки файлов в составных формах отправки.