1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【Laravel】FormRequest で Ajax と通常リクエストを判定してレスポンスを切り替える方法

1
Posted at

はじめに

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

参考記事

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?