このチュートリアルでは、プレーンな HTML と CSS でシンプルな <form> を作成し、Cloudflare Pages にデプロイします。あわせて、HTML フォームの属性と、Worker で送信データを集める方法を学びます。
このチュートリアルでは Cloudflare Pages と Workers 連携 を多用します。プラットフォームの概要は はじめに を参照してください。
ウェブでは、フォームはユーザーとウェブ文書の一般的な接点です。ユーザーがデータを入力し、多くの場合はサーバーへ送信できます。フォームは少なくとも 1 つのフォーム入力で構成され、入力はテキストフィールド、ドロップダウン、チェックボックスなどさまざまです。
各入力には name ↗ 属性で名前を付けます。サーバーが受け取ったときに、入力の値を識別できるようにするためです。さらに HTML5 では、フォーム要素に追加の属性を宣言して、自動フォーム検証を有効にできます。使える検証は入力タイプによって異なります。たとえばメールを受け付けるテキスト入力(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> は /submit エンドポイントへデータを POST します。この一連の処理は HTML5 ネイティブで、適切な form と input の属性があれば足ります。JavaScript は不要です。
フォーム要素には <label> ↗ 要素を関連づけて、各入力を明確に説明できます。見た目が分かりやすくなるのはもちろん、HTML マークアップが明確になるため、よりアクセシブルな体験にもなります。支援技術がその恩恵を直接受けます。たとえばスクリーンリーダーは、どの <input> がフォーカスされているかを読み上げられます。<label> をクリックすると、割り当てたフォーム入力にフォーカスが移るので、入力の操作領域も広がります。
これを有効にするには、入力ごとに <label> 要素を作成し、各 <input> 要素に一意の id 属性値を割り当てます。<label> には、対応する入力の一意の id 値を反映する for ↗ 属性も必要です。先ほどのスニペットを直すと、次のようになります。
<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> は現在の URL アドレスへデータを GET します。これは望ましい動作になることがほとんどありません。少なくとも、送信先 URL を action ↗ 属性で定義する必要があります。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 タイプに従います。受信側サーバーは新しい形式に合わせて、リクエストの解析方法を調整する必要があります。
このチュートリアルの残りでは、Pages 上に HTML フォームを作り、フォーム送信を受け取って解析する 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 git@github.com:<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 文書には、ユーザーが入力するいくつかのフィールドを持つフォームがあります。フォーム内に検証ルールがないため、すべてのフィールドは任意で、空のフォームも送信できます。この例では、それが意図した動作です。
HTML フォームは完成し、デプロイできる状態です。ユーザーがこのフォームを送信すると、すべてのデータが /api/submit URL への POST リクエストで送られます。これはフォームの method と action 属性によるものです。一方、現時点では /api/submit アドレスにリクエストハンドラーがありません。これから作成します。
Cloudflare Pages には Functions 機能があり、動的な動作向けに Worker を定義してデプロイできます。
Functions は functions ディレクトリに紐づき、functions のファイル構造に応じて URL リクエストハンドラーを組み立てます。たとえば 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 は FormData のキーと値のペアの JSON リストを返します。
一方、キーと値のペア(配列の配列)ではなく JSON オブジェクトで返したい場合は、自分で変換する必要があります。最近の JavaScript には Object.fromEntries ↗ ユーティリティがあります。うまくいく場合もありますが、この例の <form> には複数値を許可する movies チェックリストがあります。Object.fromEntries を使うと、生成されるオブジェクトは movies の値を 1 つだけ残し、残りを捨てます。これを避けるには、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 がすべての値を保持し、<form> 送信を正確に表す JSON レスポンスを返します。
プロジェクトをデプロイする準備ができました。
まだなら、進捗を 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 リポジトリを選んだあと、次のビルド設定でプロジェクトを構成します。
- Project name – 任意
- Production branch –
main - Framework preset – None
- Build command – None / Empty
- Build output directory –
public
Save and Deploy ボタンをクリックすると、Pages プロジェクトの初回デプロイが始まります。成功すると、固有の *.pages.dev サブドメインとライブデモへのリンクが表示されます。
このチュートリアルでは、Cloudflare Pages と Workers 連携を使って、ウェブサイトとそのバックエンドロジックを構築してデプロイしました。送信リクエストを解析する Worker ハンドラーと通信するフォーム付きの静的 HTML 文書を作成しました。
このアプリケーションの完全なソースコードは、GitHub ↗ で確認できます。