INTEGRITY Документация

Создайте HTML-форму

В этом руководстве вы создадите простой <form> с помощью обычных HTML и CSS и развернуть ее на Cloudflare Pages. По ходу дела вы узнаете о некоторых атрибутах HTML-форм и о том, как собирать отправленные данные внутри Worker.

В этом руководстве активно используются Cloudflare Pages и его интеграция с Workers. См. Руководство по началу работы руководство, чтобы ознакомиться с платформой.

Обзор

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

Каждый вход должен быть назван с использованием name атрибут, чтобы значение поля имело понятный идентификатор при получении сервером. Кроме того, с появлением HTML5 элементы формы могут объявлять дополнительные атрибуты для включения автоматической валидации формы. Доступные проверки зависят от типа поля, например текстовое поле, принимающее email-адреса (через type=email) позволяет убедиться, что значение похоже на действительный адрес электронной почты, числовое поле ввода (через type=number) принимает только целые или десятичные значения (если разрешено), а для обычных текстовых полей можно задать пользовательский pattern разрешить. Однако для любого поля можно указать, является ли значение required.

Ниже приведён пример HTML5-формы с несколькими полями и заданными для них правилами валидации:

<form method="POST" action="/api/submit">
	<input type="text" name="fullname" pattern="[A-Za-z]+" required />
	<input type="email" name="email" required />
	<input type="number" name="age" min="18" required />

	<button type="submit">Submit</button>
</form>

Если для формы HTML5 заданы правила валидации, браузеры автоматически проверяют все правила при попытке пользователя отправить форму. При наличии ошибок отправка блокируется, и браузер показывает пользователю сообщения об ошибках для исправления. Атрибут <form> будет только POST данные в /submit конечную точку, если ошибок проверки не осталось. Весь этот процесс встроен в HTML5 и требует только правильных атрибутов формы и полей ввода: JavaScript не нужен.

У элементов формы также может быть <label> элемент, связанный с ним: это позволяет чётко описать каждое поле ввода. Разумеется, здесь важна визуальная ясность, но структурированная HTML-разметка также делает интерфейс доступнее. Ассистивные технологии выигрывают от этого напрямую. Например, программы чтения с экрана могут объявить, какой <input> получает фокус. А когда <label> нажат, фокус вместо этого переходит на связанное с ним поле формы, что увеличивает область активации поля.

Чтобы включить эту функцию, создайте <label> элемент для каждого поля ввода и назначить каждому <input> элемент и уникальный id значение атрибута. <label> также должен иметь for атрибут, отражающий уникальный id значение. Изменение предыдущего фрагмента кода должно дать следующий результат:

<form method="POST" action="/api/submit">
	<label for="i-fullname">Full Name</label>
	<input
		id="i-fullname"
		type="text"
		name="fullname"
		pattern="[A-Za-z]+"
		required
	/>

	<label for="i-email">Email Address</label>
	<input id="i-email" type="email" name="email" required />

	<label for="i-age">Your Age</label>
	<input id="i-age" type="number" name="age" min="18" required />

	<button type="submit">Submit</button>
</form>

Когда этот <form> отправлена с корректными данными, её содержимое передаётся на сервер. Вы можете настроить, как и куда отправляются эти данные, указав соответствующие атрибуты самой формы. Если вы не укажете эти параметры, <form> отправит запрос GET на текущий адрес URL, что редко является желаемым поведением. Чтобы исправить это, необходимо как минимум определить action атрибут с целевым URL-адресом, но указание method также обычно рекомендуется указывать, даже если вы переопределяете значение по умолчанию для GET значение.

По умолчанию HTML-формы отправляют своё содержимое в формате application/x-www-form-urlencoded MIME-тип. Это значение будет отражено в Content-Type HTTP-заголовок, который принимающий сервер должен прочитать, чтобы определить, как разбирать содержимое данных. Вы можете настроить MIME-тип через enctype атрибут. Например, чтобы принимать файлы (через type=file), вам нужно изменить enctype к multipart/form-data значение:

<form method="POST" action="/api/submit" enctype="multipart/form-data">
	<label for="i-fullname">Full Name</label>
	<input
		id="i-fullname"
		type="text"
		name="fullname"
		pattern="[A-Za-z]+"
		required
	/>

	<label for="i-email">Email Address</label>
	<input id="i-email" type="email" name="email" required />

	<label for="i-age">Your Age</label>
	<input id="i-age" type="number" name="age" min="18" required />

	<label for="i-avatar">Profile Picture</label>
	<input id="i-avatar" type="file" name="avatar" required />

	<button type="submit">Submit</button>
</form>

Поскольку enctype изменяется, браузер также меняет способ отправки данных на сервер. Content-Type HTTP-заголовок будет отражать новый подход, а тело HTTP-запроса будет соответствовать новому MIME-типу. Принимающий сервер должен поддерживать новый формат и соответствующим образом изменить способ разбора запроса.

Пример в действии

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

Настройка

Для начала создайте новый репозиторий GitHub. Затем создайте новый локальный каталог на своем компьютере, инициализируйте git и подключите расположение GitHub в качестве удаленного репозитория:

# create new directory
mkdir new-project
# enter new directory
cd new-project
# initialize git
git init
# attach remote
git remote add origin [email protected]:<username>/<repo>.git
# change default branch name
git branch -M main

Теперь можно приступить к работе в new-project директорию, которую вы создали.

Разметка

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

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

Создайте public/index.html в каталоге вашего проекта. Все ресурсы фронтенда будут находиться в этой public директория и этот index.html файл будет служить главной страницей сайта.

Скопируйте и вставьте следующее содержимое в public/index.html файле:

<html lang="en">
	<head>
		<meta charset="utf8" />
		<title>Form Demo</title>
		<meta name="viewport" content="width=device-width,initial-scale=1" />
	</head>
	<body>
		<form method="POST" action="/api/submit">
			<div class="input">
				<label for="name">Full Name</label>
				<input id="name" name="name" type="text" />
			</div>

			<div class="input">
				<label for="email">Email Address</label>
				<input id="email" name="email" type="email" />
			</div>

			<div class="input">
				<label for="referers">How did you hear about us?</label>
				<select id="referers" name="referers">
					<option hidden disabled selected value></option>
					<option value="Facebook">Facebook</option>
					<option value="Twitter">Twitter</option>
					<option value="Google">Google</option>
					<option value="Bing">Bing</option>
					<option value="Friends">Friends</option>
				</select>
			</div>

			<div class="checklist">
				<label>What are your favorite movies?</label>
				<ul>
					<li>
						<input id="m1" type="checkbox" name="movies" value="Space Jam" />
						<label for="m1">Space Jam</label>
					</li>
					<li>
						<input
							id="m2"
							type="checkbox"
							name="movies"
							value="Little Rascals"
						/>
						<label for="m2">Little Rascals</label>
					</li>
					<li>
						<input id="m3" type="checkbox" name="movies" value="Frozen" />
						<label for="m3">Frozen</label>
					</li>
					<li>
						<input id="m4" type="checkbox" name="movies" value="Home Alone" />
						<label for="m4">Home Alone</label>
					</li>
				</ul>
			</div>

			<button type="submit">Submit</button>
		</form>
	</body>
</html>

Этот HTML документ будет содержать форму с несколькими полями для заполнения пользователем. Поскольку в форме нет правил валидации, все поля необязательны, и пользователь может отправить пустую форму. В этом примере такое поведение предусмотрено намеренно.

Worker

HTML-форма готова к развёртыванию. При отправке этой формы все данные будут отправлены в виде POST запрос к /api/submit URL. Это связано с тем, что method и action атрибуты. Однако в настоящее время обработчик запросов по адресу /api/submit адрес. Сейчас вы его создадите.

Cloudflare Pages предлагает Функции функцию, которая позволяет определять и развёртывать Workers для динамического поведения.

Functions связаны с functions директория, что удобно для формирования обработчиков URL запросов относительно functions структуру файлов. Например, functions/about.js файл будет сопоставлен с /about URL и functions/hello/[name].js будет обрабатывать /hello/:name шаблон URL, где :name обозначает любой совпадающий сегмент URL. См. Маршрутизация Functions документации.

Чтобы определить обработчик для /api/submit, необходимо создать functions/api/submit.js файл. Это означает, что ваш functions и public директории должны находиться на одном уровне, а общая структура проекта должна выглядеть примерно так:

├── functions
│   └── api
│       └── submit.js
└── public
    └── index.html

<form> отправит POST запросы, а значит, functions/api/submit.js файл должен экспортировать onRequestPost обработчик:

/**
 * POST /api/submit
 */
export async function onRequestPost(context) {
	// TODO: Handle the form submission
}

context параметр представляет собой объект с несколькими значениями, которые могут быть полезны. Для этого примера вам понадобится только Request объект, доступ к которому можно получить через context.request ключ.

Как уже упоминалось, <form> по умолчанию имеет значение application/x-www-form-urlencoded MIME-тип при отправке. А для более сложных сценариев enctype="multipart/form-data" атрибут не нужен. К счастью, оба MIME-типа можно разобрать и обработать как FormData. Это означает, что в Workers, включая Pages Functions, можно использовать нативный Request.formData парсер.

В качестве примера обработчик формы в демонстрационном приложении будет возвращать в ответе все полученные значения. Response также всегда должен возвращаться обработчиком:

/**
 * POST /api/submit
 */
export async function onRequestPost(context) {
	try {
		let input = await context.request.formData();
		let pretty = JSON.stringify([...input], null, 2);
		return new Response(pretty, {
			headers: {
				"Content-Type": "application/json;charset=utf-8",
			},
		});
	} catch (err) {
		return new Response("Error parsing JSON content", { status: 400 });
	}
}

После добавления этого обработчика пример становится полностью работоспособным. При получении отправки Worker ответит JSON-списком FormData пар «ключ-значение».

Однако если вы хотите отвечать объектом JSON вместо пар ключ-значение (массива массивов), это нужно делать вручную. Недавно в JavaScript появился Object.fromEntries утилита. В некоторых случаях это хорошо работает, однако пример <form> включает movies список флажков, допускающий несколько значений. При использовании Object.fromEntries, сгенерированный объект сохранит только один из movies значения, отбрасывая остальные. Чтобы избежать этого, необходимо написать собственный FormData к Object утилиту:

/**
 * POST /api/submit
 */
export async function onRequestPost(context) {
	try {
		let input = await context.request.formData();

		// Convert FormData to JSON
		// NOTE: Allows multiple values per key
		let output = {};
		for (let [key, value] of input) {
			let tmp = output[key];
			if (tmp === undefined) {
				output[key] = value;
			} else {
				output[key] = [].concat(tmp, value);
			}
		}

		let pretty = JSON.stringify(output, null, 2);
		return new Response(pretty, {
			headers: {
				"Content-Type": "application/json;charset=utf-8",
			},
		});
	} catch (err) {
		return new Response("Error parsing JSON content", { status: 400 });
	}
}

Приведённый выше фрагмент кода позволяет Worker сохранить все значения и вернуть JSON-ответ, точно отражающий <form> отправка.

Развёртывание

Теперь можно развернуть проект.

Если вы еще этого не сделали, сохраните прогресс в git и затем отправьте коммит (или несколько коммитов) в репозиторий GitHub:

# Add all files
git add -A
# Commit w/ message
git commit -m "working example"
# Push commit(s) to remote
git push -u origin main

Теперь ваш код находится в репозитории GitHub, а значит, Pages тоже имеет к нему доступ.

Если это ваш первый проект Cloudflare Pages, ознакомьтесь с Руководство по началу работы для полного пошагового руководства. После выбора нужного репозитория GitHub необходимо настроить проект со следующими параметрами сборки:

После нажатия на Save and Deploy кнопку, ваш проект Pages начнёт первый деплой. В случае успеха вам будет предоставлен уникальный *.pages.dev поддомен и ссылку на демонстрацию вашего проекта.

В этом руководстве вы создали и развернули сайт вместе с серверной логикой с помощью Cloudflare Pages и его интеграции с Workers. Вы создали статический HTML-документ с формой, которая взаимодействует с обработчиком Worker для разбора запросов на отправку.

Если вы хотите просмотреть полный исходный код этого приложения, вы можете найти его на GitHub.