JavaScriptのfetch APIを用いてサーバにPOSTリクエストを出したい。
その際にHTMLのformの情報をまとめて送信する方法を教えて欲しい。
こんなお悩みを解決します。
最近のWebブラウザにはfetch APIと呼ばれる JavaScriptインタフェースが用意されており、サーバと非同期に通信することが可能となっています。
多くの場合、application/json; chatset=utf-8
というJSON形式でやり取りしますが、状況によってはFormの情報をそのまま送信したい場合があります。
今回は、fetch APIを用いてFormの情報を送信する方法について解説します。
今回紹介する方法は、非同期通信でのFormの送信やサーバサイドでFormのバリデーションチェックなどを利用したい場合に有効に機能すると思います。
Webブラウザで動作するコードの実装例とともに解説するので、興味がある方はぜひ最後までご覧ください。
前提
今回の想定として、以下のようなFormの情報をfetch APIを用いて送信することを考えます。
また、動作環境としてGoogle Chromeを想定します。
クライアントサイドの実装
<form method="POST" action="/login/" id="login-form">
<input type="hidden" name="csrfmiddlewaretoken" value="abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789">
<p>
<label for="id_username">ユーザー名:</label>
<input type="text" name="username" autofocus="" autocapitalize="none" autocomplete="username" maxlength="128" required="" id="id_username">
</p>
<p>
<label for="id_password">パスワード:</label>
<input type="password" name="password" autocomplete="current-password" required="" id="id_password">
</p>
<button type="submit" id="login-button">log in</button>
</form>
ちなみに、上記は、Djangoのテンプレートを用いて生成されたHTMLを参考にしています。
<form method="POST" action="{% url 'registration:login' %}" id="login-form">
{% csrf_token %}
{{ form.as_p }}
<button type="submit" id="login-button">log in</button>
</form>
サーバサイド(Django)
Djangoによるサーバサイドの実装例は以下のようになります。
下記に示す通り、サーバサイドでは、JSON形式でデータを返却することを想定します。
from django.contrib.auth import login as auth_login
from django.contrib.auth.views import LoginView
from django.http import JsonResponse
class CustomLoginView(LoginView):
template_name = 'registration/login.html'
def form_valid(self, form):
user = form.get_user()
auth_login(self.request, user)
return JsonResponse({
'status': 200,
'message': 'Logged in'
})
def form_invalid(self, form):
return JsonResponse({
'status': 401,
'message': 'Unauthorized'
})
fetch APIによるデータの送信
fetch APIを用いてFormの情報を送信する際は、FormData
というクラスを利用します。
今回は、クライアントサイド(ここでは、Google Chrome)を想定していますが、Node.jsなどサーバサイドで対応する場合、form-data
というライブラリが必要になります(詳細は後述します)。
実装例は以下のようになります。
'use strict';
(function () {
const getCookie = (name) => {
if (document.cookie && document.cookie !== '') {
for (const cookie of document.cookie.split(';')) {
const [key, value] = cookie.trim().split('=');
if (key === name) {
return decodeURIComponent(value);
}
}
}
return undefined;
};
document.querySelector('#login-button').addEventListener('submit', (event) => {
event.preventDefault();
const loginForm = document.querySelector('#login-form');
const formData = new FormData(loginForm);
const method = loginForm.method;
const url = loginForm.action;
// Djangoを利用する場合、CSRFトークンをヘッダーに埋め込む必要があるため、cookieからトークンを取得する
const csrfToken = getCookie('csrftoken');
if (!url || !method || (undefined === csrfToken)) {
return;
}
fetch(url, {
method: method,
body: formData,
headers: {
'X-CSRFToken': csrfToken,
},
}).then((response) => {
return response.json();
}).then((response) => {
const status_code = response.status;
if (200 === status_code) {
console.log(response.message); // 'Logged in'
}
else {
console.log(response.message); // 'Unauthorized'
}
}).catch((err) => {
console.log(err);
});
});
}());
Formの情報を生成している箇所
上記のJavaScriptのうち、今回の解説対象となる部分は以下のようになります。
const loginForm = document.querySelector('#login-form');
const formData = new FormData(loginForm);
formタグに割り当てたid
を用いてDOM要素を取得し、FormData
のインスタンスを生成します。
これだけで、formの情報をfetch APIで送信する準備が整いました。
また、今回はContent-Type
を省略していますが、明示的に示す場合、headers
に以下を追記します。
headers = {
'Content-Type': 'multipart/form-data', // 追加部分
'X-CSRFToken': csrfToken,
}
今回のように、FormDataのインスタンスをbody
に指定すると、fetch API側で自動的にContent-Type
を指定してくれます。
Node.jsを利用する場合
Node.jsを利用する場合、fetch APIやFormDataを利用できないため、ライブラリを利用します。
下記のコマンドによりインストールできます。
npm install form-data
npm install node-fetch
Node.jsで動かす場合の実装例は、以下のようになります。
const fetch = require('node-fetch');
const FormData = require('form-data');
const formData = new FormData();
const csrfToken = 'csrftoken'; // 何らかの方法で取得する(Expressやhttpなど、状況に応じて対応が異なるため、取得方法は省略)
formData.append('username', 'hoge');
formData.append('password', 'pass');
formData.append('csrfmiddlewaretoken', csrfToken); // Djangoの場合、必須
fetch('http://example.com/login/', {
method: 'POST',
body: formData,
headers: {
'X-CSRFToken': csrfToken,
},
}).then((response) => {
return response.json();
}).then((response) => {
const status_code = response.status;
if (200 === status_code) {
console.log(response.message); // 'Logged in'
}
else {
console.log(response.message); // 'Unauthorized'
}
}).catch((err) => {
console.log(err);
});
});
まとめ
今回は、fetch APIを用いてFormの情報をそのまま送信する方法について解説しました。
今回紹介した方法は、非同期通信でのFormの送信やサーバサイドでFormのバリデーションチェックなどを利用したい場合に有効に機能すると思います。
機会があれば、ぜひ利用してみてください。