laravel

Laravel8でjQueryを使ってAjax通信を行う方法【405エラーと419エラーの対処法も合わせて紹介】

2021年8月28日

jqueryのコード

 

あなた
Laravel8系でjQueryを使ってajax通信を行いたいのですがうまくいきません。

どうしたらいいでしょうか?

 

こんな疑問に答えていきます。

なお、今回は主に「LaravelでjQueryを使ってPOST(DELETE)の通信を行う」ことをテーマに解説していきます。

 

【前提】

・Laravel8系

・jQuery 3.6

 

※なお、まだjQueryを導入できていない方はLaravelでjavascript、jQueryを使う方法を参考に、導入してください。

プログラミング言語
Laravelでjavascript、jQueryを使う方法

 

続きを見る

 

Laravel8でjQueryを使ってAjax通信を行う方法

 

今回行いたい挙動は、以下だとします。

 

・削除ボタンを押したらajax通信で非同期で削除を行う

・削除処理の成功失敗に応じて、フラッシュメッセージを表示する

 

ファイルの構成は以下のとおりです。

 

App
//コントローラー
- Http
-- Controllers
--- Api
---- Admin
----- DeleteUserController.php

//js
- resources
-- js
--- deleteconfirm.js

//サービス
- Http
-- Services
--- UserService.php

//ルーティング
- routes
-- user.php

//ビュー
- resources
-- views
--- admin
---- users
----- index.blade.php

 

では、jsと、phpの各ファイルの中身を解説していきます。

 

js(jQuery)側

 

js(jquery側)のコードは以下のような感じになります。

ちなみに、「delete_button_10」というようなid属性を持ったボタンをクリックした時に処理が走るようにします。

(後ほど、view側のコードも紹介します。)

 

$( function ()
{
    $( "[id^=delete_button]" ).on( "click", function ()
    {
        const id = $( this ).attr( "id" ).substr( 14 );//delete_button_IDのIDだけ切り出す
        console.log( id );
        //csrf対策
        $.ajaxSetup( {
            headers: {
                'X-CSRF-TOKEN': $( 'meta[name="csrf-token"]' ).attr( 'content' )
            }
        } );
        if ( confirm( '本当に削除してもいいですか?' ) )
        {
            $.ajax( {
                type: "post", //形式
                url: `/admin/users/api/delete/${ id }`, //リクエストURL
                dataType: 'json',
                data: { "id": id, "_method": "DELETE" } //deleteメソッドを追加
            } )
                //成功時の処理
                .done( ( res ) =>
                {
                    if ( res[ "status" ] == "success" )
                    {
                                                //view側の削除したいHTML要素のidは「row_数字(ID)」とする
                        $( `#row_${ id }` ).remove();
                        flashMessage( id, res[ "status" ], res[ "message" ] );
                    }

                } )
                                //失敗時の処理
                .fail( function ( jqXHR, textStatus, errorThrown )
                {
                    console.log( jqXHR );
                    console.log( textStatus );
                    console.log( errorThrown );
                    flashMessage( id, 'error', '削除に失敗しました。' );
                } );
        }
    } );
    /**
     * 処理終了時にフラッシュメッセージを表示
     * @param {int} id
     * @param {str} status
     * @param {str} message
     */
    function flashMessage ( id, status, message )
    {
        let bgColor = 'bg-red-300';
        let dom = `<div id ="flash_${ id }" class="${ bgColor } w-1/2 mx-auto mb-4 p-2 text-white">
           ${ message }
        </div>`;
        if ( status == "error" )
        {
            $( ".container" ).append( dom );
        } else
        {
            bgColor = 'bg-blue-300';
            dom = `<div id ="flash_${ id }" class="${ bgColor } w-1/2 mx-auto mb-4 p-2 text-white">
           ${ message }
        </div>`;
            $( ".container" ).append( dom );
        }
        //2秒後に消す
        setTimeout( function ()
        {
            $( `#flash_${ id }` ).remove();
        }, 2000 );
    }
} )

 

view側のコードは次のようになっています。

 

....省略... 
<meta name="csrf-token" content="{{ csrf_token() }}">
....省略...
<div class="container">
....省略...
<tbody>
    @foreach ($users as $user)
        <tr id="row_{{ $user->id }}">
            <td class="px-4 py-3"> {{ $user->name }}</td>
            <td class="px-4 py-3"> {{ $user->email }}</td>
            <td class="px-4 py-3">{{ $user->created_at }}</td>
            <td class="px-4 py-3">
                <button onclick="location.href='{{ route('admin.users.edit', ['user' => $user->id]) }}'"
                    class="text-white bg-indigo-400 border-0 py-2 px-4 focus:outline-none hover:bg-indigo-500 rounded">編集</button>
            </td>
            <td class="px-4 py-3">
                <button id="delete_button_{{ $user->id }}"
                    class="text-white bg-red-400 border-0 py-2 px-4 focus:outline-none hover:bg-red-500 rounded">削除</button>
            </td>
        </tr>
    @endforeach
</tbody>
....省略...

 

ちなみに、view側には以下のコードは必ず埋め込んでおいてください。

 

 <meta name="csrf-token" content="{{ csrf_token() }}">

 

csrf対策のものです。

こちらがないと通信時に419エラーになります。

 

ajax通信の雛形

 

さきほど紹介したjsファイルの中の以下の部分がajax通信の雛形です。

 

$.ajax( {
         type: "post", //形式
         url: `/admin/users/api/delete/${ id }`, //リクエストURL
         dataType: 'json', //データタイプ
         data: { "id": id, "_method": "DELETE" } //deleteメソッドを追加
  } )

 

削除処理の場合はdataキーに「"_method": "DELETE"」を指定してあげるとうまくいきます。

これがないと「405 (Method Not Allowed)」で怒られます。

 

PHP(laravel)側①ルーティングファイル

 

php側のコードを紹介していきます。

今回は、「ドメイン/admin/users/api/delete/削除対象のユーザID」にリクエストをしたら削除をしたいので、まずはそちらを明示するルーティングファイルを編集します。

コードは以下のとおりです。

 

//API
Route::delete('/users/api/delete/{id}', [DeleteUserController::class, 'delete'])
    ->middleware('auth:admins')
    ->name('users.delete');

 

こちらの記述をすることで、リクエストが来た場合に「DeleteUserController」の「deleteメソッド」が走ることになります。

 

ルーティングファイルの注意点

 

削除処理の場合ルーティングファイルで「Route::delete」としてあげてくださいね。

ここをpostとかにすると「405 (Method Not Allowed)」で怒られます。

削除 → Route::delete

更新 → Route::put

です。

 

PHP(laravel)側②コントローラー

 

次にコントローラー側の記述です。

今回は次のように記述しました。

 

<?php

namespace App\Http\Controllers\Api\Admin;

use App\Http\Controllers\Controller;
use App\Http\Services\UserService;

class DeleteUserController extends Controller
{
    /**
     * @var UserService
     */
    protected $userService;

    public function __construct(UserService $userService)
    {
        $this->middleware('auth:admins');
        $this->userService = $userService;
    }

    public function delete($id)
    {
        //ユーザーの削除を行う
        try {
            $this->userService->delete($id);
            $status = 'success';
            $message = '削除が完了しました。';
            $returnArr = [
                'status' => $status,
                'message' => $message
            ];
            return response()->json($returnArr);
        } catch (\Throwable $th) {
            $status = 'error';
            $message = '削除に失敗しました。';
            $returnArr = [
                'status' => $status,
                'message' => $message
            ];
            return response()->json($returnArr);
        }
    }
}

 

ajax通信で受け取った対象idを、userServiceクラスに渡してあげて、処理を走らせる内容になっています。

注意点は、レスポンスを返す時の形です。

json形式で返してあげないとエラーになります。

「 return response()->json($returnArr);」という記述の部分でjsonで返しています。

 

PHP(laravel)側③サービスクラス

 

最後にサービスクラスです。

コードは次の通り。

 

<?php

namespace App\Http\Services;

use Illuminate\Support\Facades\Hash;
use App\Models\User;
use Illuminate\Support\Carbon;

class UserService
{
    public function __construct()
    {
    }

    /**
     * ユーザー削除
     */
    public function delete($id)
    {
        $user = User::findOrFail($id);
        $user->deleted_at = Carbon::now()->format("Y-m-d H:i:s");
        $user->save();
    }
}

 

今回の自分の実装では、削除は物理削除ではなく、deleted_atに日付を入れるだけとしています。

別に物理削除でも構いません。

delete()のメソッドはお好きにカスタマイズしてみてください。

 

PHP側のコードの紹介、解説は以上になります。

 

Laravel×Ajaxで405エラー、419エラーが出る時の対処法

 

コードの解説時にもいくつか紹介しましたが、LaravelでjQueryを使ったAjax通信を実装する時によく出てくる405エラーと419エラーの解決方法を、それぞれ紹介します。

 

405 (Method Not Allowed)

 

メソッド(post、get、delet、put)が許可されていない場合に起きるエラーです。

このエラーが出た場合は以下を確認してください。

 

・jQuery側 → ajaxのdataキーで「 "_method": "実行したいメソッド"」を記述していること 

・php側 → ルーティングファイルで実行したいメソッドを指定していること(Route::post、Route::deleteなど)

 

419 (unknown status)

 

こちらはcsrfの対策ができていない場合に起きるエラーです。

次のことを確認してください。

 

・jQuery側 → ajax通信の前にheaderにcsrfトークンを設定していること(設定するためのコードはこの記事のコード紹介のところに載せてあります)

・blade.php側 → meta name属性でcsrfの記述。「<meta name="csrf-token" content="{{ csrf_token() }}">」を設定してください。

 

補足:csrfとは

 

補足的に説明です。

csrfとはクロスサイトリクエストフォージェリの略称です。

 

クロスサイトリクエストフォージェリは、認証済みユーザーに代わって不正なコマンドを実行する、悪意のある攻撃の一種です。

 

公式ドキュメントでは上記のように解説されています。

要は「悪意をもった第三者が不正にあなたの情報を利用するための攻撃」だと認識しておけばOKです。

攻撃の細かい仕組みを知りたい方はIPAの公式記事が参考になると思います。

 

ということで今回は、LaravelでjQueryを使ってAjax通信を行う場合の解説でした。

 

-laravel

© 2021 エンジニアてんし君ブログ