はじめに
LaravelのFormRequestを使ってバリデーションを行う際、バリデーション失敗時に「Ajaxリクエスト」か「通常のフォーム送信」かでレスポンスを変えたいケースがありました。
例えば、
- 非同期リクエストでは JSON でエラーレスポンスを返したい
- 通常の POST 送信ではリダイレクトしたい
そんな対応を行ったので、本記事では備忘録としてまとめます。
環境
- Laravel Framework 12.21.0(6.x以上で利用可能)
判定に使えるメソッド
expectsJson()
内部的に wantsJson() や X-Requested-With を考慮して、レスポンスを JSON にすべきかどうかを包括的に判定する。
基本的にはこれを使うのがベストプラクティス
ajax()
RequestHeaderにX-Requested-With: XMLHttpRequestが含まれているか判定する。
jQuery など古い Ajax 通信の判定用で、fetch/axios を使うモダンな実装では非推奨
wantsJson()
RequestHeaderにAccept: application/jsonが含まれているか判定する。
※レスポンスとして JSON を欲しているかどうか を見たいときに便利
isJson()
RequestHeaderにContent-Type: application/jsonが含まれているか判定する。
※リクエストボディが JSON かどうか を見たいときに便利
使用例
resources/view/Example/index.blade.php
<script>
const handleSubmit = () => {
const form = document.forms[0];
const formData = new FormData(form);
const isFetch = document.querySelector("#isFetch").checked;
// Ajax送信
if(isFetch) {
return submitFormAjax(formData);
}
// 通常フォーム送信
form.requestSubmit();
}
const submitFormAjax = async (formData) => {
try {
const response = await fetch(
"{{ route('example.submit') }}",
{
method: "POST",
headers: {
"Accept": "application/json",
"Content-Type": "application/json",
"X-CSRF-TOKEN": document.querySelector("meta[name='csrf-token']").getAttribute("content"),
"X-Requested-With": "XMLHttpRequest",
},
body: JSON.stringify(Object.fromEntries(formData.entries())),
}
);
const data = await response.json();
if(!response.ok) {
throw new Error(`Error: ${data.errors}`);
}
console.log("Data:", data);
} catch (error) {
console.error("Error:", error);
}
}
</script>
app/Http/Requests/ExampleRequest.php
/**
* Handle a failed validation attempt.
* @throws \Illuminate\Validation\HttpResponseException
* @see \Illuminate\Foundation\Http\FormRequest::failedValidation()
*/
protected function failedValidation(
\Illuminate\Contracts\Validation\Validator $validator
): void {
// AjaxやAPIの場合
if ($this->expectsJson()) {
throw new \Illuminate\Http\Exceptions\HttpResponseException(
response()->json([
'errors' => $validator->errors()->toArray(),
'data' => [],
], 422)
);
}
// 通常フォーム送信の場合
parent::failedValidation($validator);
}
おわりに
- FormRequest の
failedValidation()をオーバーライドすると、バリデーション失敗時のレスポンスを制御可能 - 基本的には
expectsJson()を使えばOK