Учебник Express часть 6: Работа с формами

В этой главе мы покажем вам как работать с HTML формами в Express, используя Pug, и в частности как написать формы для создания, обновления и удаления документов из базы данных.

Предварительные знания: Завершите изучение предыдущих тем учебника, включая Учебник Express Часть 5: Отображение данных библиотеки
Цель: Понять, как писать формы для получения данных от пользователей и обновлять базу данных с этими данными.

Обзор

HTML форма - это группа из одного или нескольких полей / виджетов на веб-странице, которая может использоваться для сбора информации от пользователей для отправки на сервер. Формы представляют собой гибкий механизм для сбора данных, вводимых пользователем, поскольку существуют подходящие входные данные форм, доступные для ввода различных типов данных-текстовые поля, флажки, переключатели, средства выбора даты и т. д. Формы также являются относительно безопасным способом обмена данными с сервером, поскольку они позволяют отправлять данные в запросах POST с защитой от подделки межсайтовых запросов.

Работа с формами может быть сложной! Разработчику нужно написать HTML код для форм, валидацию и правильно анализировать введённые данные на сервере (и, возможно, также в браузере), отобразить форму с сообщениями об ошибках, чтобы сообщить пользователям о любых недопустимых полях, обработать данные, когда они были успешно отправлены, и, наконец, каким-то образом ответить пользователю о том, что результат успешен.

В этом уроке мы покажем вам, как вышеуказанные операции могут быть выполнены в Express. По пути мы расширим веб-сайт LocalLibrary, чтобы пользователи могли создавать, редактировать и удалять элементы из библиотеки.

Примечание: Мы не рассматривали, как ограничить определённые маршруты аутентифицированными или авторизованными пользователями, поэтому на данный момент любой пользователь сможет вносить изменения в базу данных.

HTML Forms

Первый краткий обзор HTML Forms. Рассмотрим простую HTML-форму с одним текстовым полем для ввода имени некоторой "команды" и связанной с ней меткой:

Simple name field example in HTML form

Определённые в HTML формы собираются внутри тэга <form>...</form>, содержащего хотя ы один элемент input с type="submit".

html
<form action="/team_name_url/" method="post">
  <label for="team_name">Enter name: </label>
  <input
    id="team_name"
    type="text"
    name="name_field"
    value="Default name for team." />
  <input type="submit" value="OK" />
</form>

Хотя здесь мы включили только одно (текстовое) поле для ввода имени команды, форма может содержать любое количество других элементов ввода и связанных с ними меток. Атрибут type определяет какой из виджетов будет выбран для отображения поля. Атрибуты name и id идентифицируют поле в JavaScript/CSS/HTML, а value определяет его первоначальное значение. Связанная с полем метка, задаётся с помощью тега label (располагается строкой выше и содержит в себе подпись "Enter name"). Связь метки и поля ввода устанавливается при помощи атрибута for, в котором указывается значение идентификатора поля (input id).

Input submit будет отображаться в виде кнопки (по умолчанию) - он может быть нажат пользователем, чтобы загрузить данные, содержащиеся в других входных элементов на сервер (в данном случае, только team_name). Атрибуты формы определяют метод HTTP, используемый для отправки данных, и назначение данных на сервере (action):

  • action: ресурс/URL-адрес, по которому данные должны отправляться на обработку при отправке формы. Если это не установлено (или установлено в пустую строку), то форма будет отправлена назад к URL текущей страницы.
  • method: Метод HTTP, используемый для отправки данных: POST or GET.
    • Метод POST должен всегда использоваться, если данные собираются привести к изменению базы данных сервера, потому что это может быть сделано более устойчивым к атакам запроса подделки межсайтового.
    • Метод GET следует использовать только для форм, которые не изменяют пользовательские данные (например, форма поиска). Рекомендуется, когда вы хотите, чтобы иметь возможность делать закладки или поделиться URL.

Процесс обработки формы

Обработка форм использует все те же методы, которые мы изучили для отображения информации о наших моделях: маршрут отправляет запрос в функцию контроллера, которая выполняет все необходимые действия с базой данных, включая чтение данных из моделей, а затем генерирует и возвращает HTML-страницу. Что усложняет ситуацию, так это то, что сервер также должен иметь возможность обрабатывать данные, предоставленные пользователем, и повторно отображать форму с информацией об ошибках, если есть какие-либо проблемы.

Блок-схема процесса обработки запросов формы показана ниже, начиная с запроса страницы, содержащей форму (показана зелёным цветом):

Как показано на диаграмме выше, основные действия, которые необходимо выполнить коду обработки форм:

  1. Отображение формы по умолчанию при первом запросе пользователем.
    • Форма может содержать пустые поля (например, если вы создаёте новую запись), или она может быть предварительно заполнена начальными значениями (например, если вы изменяете запись или имеете полезные начальные значения по умолчанию).
  2. Получение данных, отправленных пользователем, обычно в запросе HTTP POST.
  3. Валидация и очистка данных.
  4. Если какие-либо данные недопустимы, повторно отобразите форму—на этот раз с заполненными пользователем значениями и сообщениями об ошибках для проблемных полей
  5. Если все данные верны, выполнить требуемые действия (например, сохранить данные в базе данных, отправьте уведомление по электронной почте, возвращающие результат поиска, загрузить файл и т. д.)
  6. После завершения всех действий перенаправьте пользователя на другую страницу.

Часто код обработки формы реализуется с помощью GET route для начального отображения формы и POST route к тому же пути для обработки проверки и обработки данных формы. Это подход, который будет использоваться в этом уроке!

Сам Express не предоставляет какой-либо конкретной поддержки для операций обработки форм, но он может использовать промежуточное программное обеспечение для обработки POST и GET параметров из формы, а также для проверки/очистки их значений.

Валидация и обработка

Перед сохранением данных формы их необходимо проверить и очистить:

  • Проверка проверяет, что введённые значения являются подходящими для каждого поля (расположены в правильном диапазоне, формат и т. д.) и что значения были предоставлены для всех обязательных полей.
  • Очистка удаляет / заменяет символы в данных, которые потенциально могут использоваться для отправки вредоносного содержимого на сервер.

В этом уроке мы будем использовать популярный модуль express-validator для проверки и очистки данных формы.

Установка

Установите модуль, выполнив следующую команду в корне проекта

bash
npm install express-validator

Использование express-validator

Примечание: express-validator руководство на Github предоставляет хороший обзор API. Мы рекомендуем вам прочитать это, чтобы получить представление о всех его возможностях (включая создание пользовательских валидаторов). Ниже мы рассмотрим только подмножество, которое полезно для LocalLibrary.

Для того, чтобы использовать валидатор в наших контроллерах, мы должны требовать функции, которые мы хотим использовать из модулей 'express-validator/check' и 'express-validator/filter', как показано ниже:

js
const { body, validationResult } = require("express-validator/check");
const { sanitizeBody } = require("express-validator/filter");

Есть много доступных функций, позволяющих проверять и очищать данные из параметров запроса, тела, заголовков, файлов cookie и т. д., или все сразу. Для этого урока мы будем использовать body, sanitizeBody, and validationResult (как "требуется" выше).

Функции определяются следующим образом:

  • body(fields[, message]): Задаёт набор полей в теле запроса (параметр POST) для проверки, а также необязательное сообщение об ошибке, которое может отображаться в случае сбоя тестов. Критерии проверки последовательно связаны с методом body(). Например, первая проверка ниже проверяет, что поле" имя "не пустое и задаёт сообщение об ошибке" пустое имя", если оно не пустое. Второй тест проверяет, что поле age является допустимой датой, и с помощью optional() указывает, что пустые и пустые строки не пройдут проверку.
    js
    body('name', 'Empty name').isLength({ min: 1 }),
    body('age', 'Invalid age').optional({ checkFalsy: true }).isISO8601(),
    
    Можно также последовательно подключить различные валидаторы и добавить сообщения, отображаемые при выполнении предыдущих валидаторов.
  • js
    body('name').isLength({ min: 1 }).trim().withMessage('Name empty.')
        .isAlpha().withMessage('Name must be alphabet letters.'),
    

    Примечание: Вы также можете добавить встроенные средства очистки, такие как trim(), как показано выше. Однако средства очистки, применяемые здесь, применяются только к шагу проверки. Если требуется очистить конечный результат, необходимо использовать отдельный метод очистки, как показано ниже.

  • sanitizeBody(fields): Задаёт поле тела для очистки. затем операции очистки последовательно соединяются с этим методом. Например, операция очистки escape(), описанная ниже, удаляет символы HTML из переменной name, которые могут использоваться в атаках сценариев между сайтами JavaScript.
    js
    sanitizeBody('name').trim().escape(),
    sanitizeBody('date').toDate(),
    
  • validationResult(req): Запускает проверку, делая ошибки доступными в виде объекта результата проверки. Это вызывается в отдельном обратном вызове, как показано ниже:
    js
    (req, res, next) => {
      // Extract the validation errors from a request.
      const errors = validationResult(req);
    
      if (!errors.isEmpty()) {
        // There are errors. Render form again with sanitized values/errors messages.
        // Error messages can be returned in an array using `errors.array()`.
      } else {
        // Data from form is valid.
      }
    };
    
    Мы используем метод isEmpty() результата проверки, чтобы проверить, были ли ошибки, и его метод array (), чтобы получить набор сообщений об ошибках. Дополнительные сведения см. в разделе API результатов проверки.

Цепочки проверки и очистки являются промежуточными запросами, которые должны быть переданы обработчику Express -маршрута (мы делаем это косвенно, через контроллер). При запуске промежуточного по каждый валидатор / средства очистки выполняется в указанном порядке..

Мы рассмотрим некоторые реальные примеры, когда мы реализуем LocalLibrary формы ниже.

Дизайн формы

Многие модели в библиотеке связаны / зависимы—например, книга требует автора, а также может иметь один или несколько жанров. Это поднимает вопрос о том, как мы должны обрабатывать случай, когда пользователь хочет:

  • Создайте объект, если связанные с ним объекты ещё не существуют (например, книга, в которой не определён объект автора).
  • Удаление объекта, который все ещё используется другим объектом (например, удаление жанра, который все ещё используется книгой).

Для этого проекта мы упростили реализацию, объявив, что форма может быть только:

  • Создайте объект, используя объекты, которые уже существуют (таким образом, пользователи должны будут создать все необходимые экземпляры автора и жанра, прежде чем пытаться создать любые объекты книги).
  • Удалите объект, если на него не ссылаются другие объекты (например, вы не сможете удалить книгу, пока не будут удалены все связанные объекты BookInstance).

Примечание: Более" надёжная " реализация может позволить создавать зависимые объекты при создании нового объекта и удалять любой объект в любое время (например, путём удаления зависимых объектов или путём удаления ссылок на удалённый объект из базы данных).

Маршруты

Чтобы реализовать наш код обработки форм, нам понадобятся два маршрута с одинаковым шаблоном URL. Первый (GET) маршрут используется для отображения новой пустой формы создания объекта. Второй маршрут (POST) используется для проверки введённых пользователем данных, а затем сохранения информации и перенаправления на страницу сведений (если данные верны) или повторного отображения формы с ошибками (если данные неверны).

Мы уже создали маршруты для всех страниц создания нашей модели в /routes/catalog.js (in a previous tutorial). Например, жанровые маршруты показаны ниже:

js
// GET request for creating a Genre. NOTE This must come before route that displays Genre (uses id).
router.get("/genre/create", genre_controller.genre_create_get);

// POST request for creating Genre.
router.post("/genre/create", genre_controller.genre_create_post);

Express формы — подразделы

В следующих подразделах мы добавим необходимые формы для нашего веб-сайта. Вы должны прочитать и проработать каждый из них по очереди, прежде чем перейти к следующему.

  1. Форма для создания Genre (en-US) — Определение нашей страницы для создания объектов Genre.
  2. Форма для создания Author (en-US) — Определение страницы для создания объектов Author.
  3. Форма для создания Book (en-US) — Определение страницы/формы для создания объектов Book.
  4. Форма для создания BookInstance — Определение страницы/формы для создания объектов BookInstance.
  5. Форма для удаления Author — Определение страницы для удаления объектов Author.
  6. Форма для обновления Book — Определение страницы для обновления объектов Book.

Испытайте себя

Реализуйте страницы удаления для моделей Book, BookInstance и Genre, сделав на них ссылки с соответствующих страниц с подробными сведениями таким же образом, как это было нами сделано для страницы Delete Author («Удалить автора»). Эти страницы должны следовать тем же конструктивным принципам:

  • Если на объект, указанный к удалению, существуют ссылки из других объектов, то должен быть выведен список этих других объектов вместе с сообщением о том, что соответствующая запись в БД не может быть удалена до тех пор, пока не будут удалены объекты из списка.
  • Если на объект, указанный к удалению, нет ссылок из других объектов, то на визуальном представлении страницы должен быть выведен вопрос о подтверждении удаления этого объекта. Если пользователь подтвердит удаление нажатием кнопки Delete, запись должна быть удалена.

Несколько подсказок:

  • Удаление объекта модели Genre очень похоже на удаление объекта модели Author, так как на каждый из этих объектов могут быть ссылки из объектов модели Book (таким образом, в обоих этих случаях вы можете удалить объект только тогда, когда будут удалены объекты модели Book, из которых есть ссылки на объект, указанный к удалению).
  • Удаление объекта модели Book тоже похоже на удаление объекта модели Author, но в этом случае вам нужно будет проверять, нет ли объектов модели BookInstance, из которых есть ссылки на объект, указанный к удалению.
  • Удаление объекта модели BookInstance самое простое из всех, потому что не существует объектов, из которых могут быть ссылки на объект модели BookInstance. В данном случае вы можете просто найти соответствующую запись и удалить её.

Реализуйте страницы обновления для моделей BookInstance, Author и Genre, сделав на них ссылки с соответствующих страниц с подробными сведениями таким же образом, как это было нами сделано для страницы Update Book («Обновить книгу»).

Несколько подсказок:

  • Страница Update Book («Обновить книгу»), которую мы только что реализовали, является самой сложной в реализации! Те же приёмы можно использовать для страниц обновления объектов других моделей.
  • Свойства объектов модели Author с датой рождения и датой смерти и свойство due_back объектов модели BookInstance имеют неподходящий формат для ввода в поле формы с типом 'date' (оно требует ввод даты в виде строки в формате "YYYY-MM-DD"). Самый простой способ это обойти — создать новое виртуальное свойство в соответствующей модели для каждого реального свойства с датой, которое преобразует и возвратит дату в нужном формате, а затем использовать это виртуальное свойство в соответствующих шаблонах страниц.
  • Если вы зашли в тупик, обратитесь к реализации страниц обновления в примере вот тут.

Резюмируем

Express, Node.js и сторонние пакеты, полученные с помощью npm, обеспечивают всё, что вам потребуется для добавления форм на ваш сайт. В этой статье вы узнали, как создавать формы с помощью языка написания шаблонов Pug, как выполнять валидацию и очистку вводимых пользователем данных с помощью пакета express-validator и как добавлять, удалять и модифицировать записи в базе данных сайта.

Теперь вы должны представлять, как добавить на свои собственные Node.js-сайты простые формы и код для их обработки!

Смотрите также

Содержание этого модуля (блока статей)