FormData によるフォームタグの適用
# フロントエンド前回の記事では、multipart/form-data のリクエストフォーマットとそれが解決しようとしている問題について説明しました。この記事では、実際の開発で遭遇する問題とその応用について解説します。
具体的には、この記事では以下のフォームの応用に関する部分を含みます:
<form/>タグの背後で行われていること- JavaScriptにおけるFormDataの応用
- FormDataと
fetchの組み合わせ - JavaScriptによるファイルアップロードの操作
再びformタグについて
HTMLのformタグには多くの詳細がブラウザによって実装されており、開発者は時々それを見落としがちです。以下の例を見てみましょう:
<form method="POST" action="/upload" enctype="multipart/form-data">
<input type="text" name="name" />
<input type="file" name="file" />
<button>
Submit
</button>
</form>
JavaScriptのコードがまったくない状態で、Submitボタンを押すと、ブラウザは以下のことを自動的に行います:
- input内のnameとfileフィールドをシリアライズする
Content-Type: multipart/form-dataのHTTPリクエストをPOSTメソッドで送信する- ファイルを読み込み、リクエストの内容に追加する(ファイルが存在する場合)
シングルページアプリケーションやフロントエンドフレームワークが普及する前は、フォームにデータを入力して送信し、その後別のページにリダイレクトするのが一般的な方法でした。しかし、入力するデータが増えたり、一部の領域だけを更新する必要がある場合(例えばコメントなど)、毎回全ページを更新することはユーザーにとってあまり良い体験ではないため、次第にAJAXを介してAPIを叩き、JavaScriptでデータを動的に更新する方法が発展しました。

動的更新の方法は確かにユーザー体験を改善しましたが、良いフォームデザインを実現するには多くの詳細に配慮する必要があります:
- エラーハンドリング
- 状態遷移
- データ保存
- アクセシビリティ
多くの場合、一つの環境がうまくいかないと、ユーザーはむしろシンプルな全ページ更新のフォームタグを使いたがります。特にバックエンドアプリケーションの場合、<form>タグを使用して全ページ更新を行うことで、開発時間を大幅に節約でき、ブラウザの内蔵メカニズムを利用することで、より安定して動作することが多いです。
FormDataのJavaScriptにおける応用
FormDataは、開発者がkey/valueのアプリケーションを簡単に扱えるインターフェースを定義しており、最も一般的なのはフォーム処理です。FormDataの宣言は以下のように書けます:
const formData = new FormData()
// key value
formData.append('name', 'Kalan');
formの要素をFormDataに入れると、自動的に中の情報がシリアライズされてFormDataになります:
<form id="form" enctype="multipart/form-data" action="/upload" method="POST">
<input type="text" name="name" />
<input type="file" name="file" />
<button>Submit</button>
</form>
<script>
const formData = new FormData(document.getElementById('form'));
formData.get('name'); // 現在のinputの値を取得
formData.get('file'); // 現在のファイルを取得
</script>

さらに、FormDataをfetchのbodyに入れると、ブラウザは自動的にmultipart/form-data形式で送信してくれます:
const formData = new FormData();
formData.append('name', 'Kalan');
formData.append('file', new File(['Hello World'], 'file.txt', { type: 'text/plain' }))
fetch('/upload', {
method: 'POST',
body: formData
})
上記のJavaScriptコードを実行し、Networkのリクエストを観察すると、特にContent-Typeを定義していなくても、ブラウザが自動的にmultipart/form-data形式で送信し、form dataのシリアライズもブラウザによって行われることがわかります。

まとめ
この二つの記事では、multipart/form-dataの応用と実際の使用方法について説明しました。第一の記事では、Content-Dispositionの意味、boundaryの用途、そしてmultipart/form-dataのリクエストがどのように構築されるかについて説明しました。第二の記事では、実務においてフォームとFormDataをどのように使用し、JavaScriptを介してFormDataの処理とファイルアップロードを行うかについて説明しました。
サーバーサイドにとって、ウェブ上でファイルアップロードを行うことも一つのHTTPリクエストであるため、サーバーはヘッダー内の情報とmultipart/form-dataで定義された形式に基づいてデータを解析する必要があります。そうしないと、正しくファイルの内容を取得することができません。通常、これらの解析はフレームワークによって処理されていますが、ここで特に強調したいのは、ウェブ上でファイルをアップロードすることには特別な魔法はなく、その背後にはHTTPリクエストが基盤となっているということです。
関連記事
- 超リンクの下線をもっときれいに見せる:text-underline-offsetデフォルトでは下線が文字にかなり近く、こういう見た目を好まないデザイナーもいる。僕自身も、あまりきれいだとは思っていない。
- なぜウェブは Pixel Perfect を追求すべきではないのかPixel Perfect が本当に重要なときだけそれを気にすべきであり、そうでなければ往々にして双方にとって損な結果になる。
- CSS の HSL で色を書こう!(そしてより良い方法)Web開発において、従来の HEX や RGB の色表現は広く使われているものの、読みやすさや直感性に欠ける問題があり、P3 のような広色域では表現力にも限界がある。HSL(色相、彩度、明度)は、より直感的に色を定義できる方法であり、開発者が色を理解し調整しやすくしてくれる。HSL は色相・彩度・明度の3つの軸で色を表すため、特にデザインシステムにおいて、カラーパレットの明度変化をより自然に扱える。
- line-heightを1およびellipsisを設定しないようにしたい本文では、なぜウェブデザインにおいて line-height を 1 に設定することが推奨されないのか、また ellipsis を使用する際に直面する言語の問題について探ります。