【Laravel8】Gateで認可処理をしたときdenyのエラーメッセージをHTMLではなくてjsonで返すようにする。

Laravel

はい、今回は、Laravel8でGateを使って認可処理をしてみたんですが、エラーメッセージがHTMLでしか返ってきませんでした。

そのためjsonで返せるようにしました。

Laravel8のGateの公式の書き方はこちら

結論 (jsonでエラーメッセージの返し方だけ知りたい人はここ見るだけでOK)

参考
https://www.messiahworks.com/archives/24038

app/Exceptions/Handler.phpに以下を追加します。

    public function render($request, $exception)
    {
        // APIの場合はエラー画面ではなく、JSONでエラーコードとメッセージを返す
        if ( $request->is('api/*') ) {
            if($this->isHttpException($exception)){
                return response()->json([
                    'error_code' => $exception->getStatusCode(),
                    'errors' => $exception->getMessage()
                ], $exception->getStatusCode());
            }
            // HTTPエラーじゃないけど、laravel的エラー。400(Bad Request)を返す
            return response()->json([
                'error_code' => 400,
                'errors' => $exception->getMessage()
            ], 400);
        }
        return parent::render($request, $exception);
    }

ほい、ではGateから知りたい人は初めから行きましょう!!!!!!

Gateの作成

Gateとは

ゲートは、ユーザーが特定のアクションを実行することを許可されているかどうかを判断する単なるクロージャです。通常、ゲートは、Gateファサードを使用してApp\Providers\AuthServiceProviderクラスのbootメソッド内で定義されます。ゲートは常に最初の引数としてユーザーインスタンスを受け取り、オプションで関連するEloquentモデルなどの追加の引数を受け取る場合があります。

--公式より--
https://readouble.com/laravel/8.x/ja/authorization.html

ま、認可のことですね!
認証はログインとかのこと、認可はそのユーザが持っている権限的な感じという認識です。

では行きましょう!!!!

migrationファイルの確認

以下がmigrationファイルになります。

    public function up()
    {
        Schema::create('users', function (Blueprint $table) {
            $table->id();
            $table->string('name');
            $table->string('email')->unique();
            $table->timestamp('email_verified_at')->nullable();
            $table->string('password');
            $table->rememberToken();
            $table->boolean('is_admin')->default(false)->comment('trueだとadmin権限を持つ');
            $table->timestamps();
        });
    }

is_adminがtrueなのかfalseなのかをGateで確認するようにしますね。

コントローラーを確認

use Gate;

public function index()
    {
        Gate::authorize('isAdmin'); //admin権限のあるユーザかどうか確認

        //認可で問題がなかったら以下をjsonで返す。
        return response()->json([
            'success' => true,
            'message' => "success!"
        ]);
    }

Gateを作成

公式を参考にしていきますよー

use App\Models\User;
use Illuminate\Auth\Access\Response;
use Illuminate\Support\Facades\Gate;

Gate::define('edit-settings', function (User $user) {
    return $user->isAdmin
                ? Response::allow()
                : Response::deny('You must be an administrator.');
});

//公式からの参照
//https://readouble.com/laravel/8.x/ja/authorization.html?header=%25E3%2582%25B2%25E3%2583%25BC%25E3%2583%2588%25E3%2581%25AE%25E3%2583%25AC%25E3%2582%25B9%25E3%2583%259D%25E3%2583%25B3%25E3%2582%25B9

↓オリジナルでちょっと書いていく
/app/Providers/AuthServiceProvider.php

<?php

namespace App\Providers;

use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
use Illuminate\Support\Facades\Gate;

use App\Models\User; //追加
use Illuminate\Auth\Access\Response;

class AuthServiceProvider extends ServiceProvider
{
    /**
     * The policy mappings for the application.
     *
     * @var array
     */
    protected $policies = [
        //'App\Models\Model' => 'App\Policies\ModelPolicy',
    ];

    /**
     * Register any authentication / authorization services.
     *
     * @return void
     */
    public function boot()
    {
        $this->registerPolicies();

        //memoの操作をできるユーザなのか認可チェック
        //usersテーブルのis_adminがtrueならOK
        Gate::define('isAdmin', function(User $user) {
            return $user->is_admin==true
                ? Response::allow()
                : Response::deny('You must be an administrator.'); //jsonで返すmessageの内容
        });
    }
}

ほい、これでコントローラーから呼び出した際の挙動ができました。

成功だったらそのままjsonで成功のメッセージを返す。

エラーがあった際には'You must be an administrator.'という文字列を出力するようになります。

ただ、このままだとHTMLでdenyのエラーメッセージが返されるんです。

今回はjsonでエラーメッセージを返したいと思っているんですよね~

jsonでエラーを返す。

調べてみたら以下でエラーハンドリングができるっぽい。。
app/Exceptions/Handler.php

ということでrender関数を追記

    public function render($request, $exception)
    {
        // APIの場合はエラー画面ではなく、JSONでエラーコードとメッセージを返す
        if ( $request->is('api/*') ) {
            if($this->isHttpException($exception)){
                return response()->json([
                    'error_code' => $exception->getStatusCode(),
                    'errors' => $exception->getMessage()
                ], $exception->getStatusCode());
            }
            // HTTPエラーじゃないけど、laravel的エラー。400(Bad Request)を返す
            return response()->json([
                'error_code' => 400,
                'errors' => $exception->getMessage()
            ], 400);
        }
        return parent::render($request, $exception);
    }

ほい、これで検証してみると、HTML形式ではなく、json形式でメッセージが返ってくると思います!!!

まとめ

denyのエラーメッセージをjsonで返す。

ということだけに集中していたら、この方法は見つかりませんでした。

Laravel全体でエラーの返し方をjsonにするという風に考えるなど、何かを掛け合わせて考える必要がありますね。

勉強、勉強…

コメント

タイトルとURLをコピーしました