關(guān)于Laravl自定義異常處理

一青扔、版本介紹

PS C:\phpstudy_pro\WWW\api.demo.test> php artisan --version                  
Laravel Framework 10.40.0
PS C:\phpstudy_pro\WWW\api.demo.test> 

二、希望情況

  • 希望正常情況翩伪,返回:
    即:統(tǒng)一http狀態(tài)為200微猖,采用自定義code、message缘屹、data方式實(shí)現(xiàn)凛剥。


    image.png
{
    "code": 200,
    "message": "請(qǐng)求成功",
    "data": {
        "token": "125|tZmR9SOUq946dwxdRM7hlrvvGDOnnmeUaA63MYiv3519f747"
    }
}
  • 不希望異常情況返回
    雖然加了"X-Requested-With":"XMLHttpRequest"請(qǐng)求頭,可以避免返回html頁面的錯(cuò)誤轻姿,這樣瀏覽器請(qǐng)求時(shí)犁珠,可以快速定位錯(cuò)誤逻炊。但是,這樣返回的數(shù)據(jù)盲憎,狀態(tài)碼依然是http的嗅骄,不是自定義的,碰到有些需要自定義認(rèn)證的異常消息時(shí)饼疙,接口總是不一致溺森,前端處理起來非常麻煩,因此統(tǒng)一一下窑眯。
image.png
image.png
{
    "message": "Device name 不能為空屏积。",
    "errors": {
        "device_name": [
            "Device name 不能為空。"
        ]
    }
}
  • 或者希望這樣的錯(cuò)誤保持原樣磅甩。有利于開發(fā)過程中排查錯(cuò)誤炊林。


    image.png
{
    "message": "The GET method is not supported for route api/user/token. Supported methods: POST.",
    "exception": "Symfony\\Component\\HttpKernel\\Exception\\MethodNotAllowedHttpException",
    "file": "C:\\phpstudy_pro\\WWW\\api.demo.test\\vendor\\laravel\\framework\\src\\Illuminate\\Routing\\AbstractRouteCollection.php",
    "line": 122,
    "trace": [
        {
            "file": "C:\\phpstudy_pro\\WWW\\api.demo.test\\vendor\\laravel\\framework\\src\\Illuminate\\Routing\\AbstractRouteCollection.php",
            "line": 107,
            "function": "requestMethodNotAllowed",
            "class": "Illuminate\\Routing\\AbstractRouteCollection",
            "type": "->"
        },
        {
            "file": "C:\\phpstudy_pro\\WWW\\api.demo.test\\vendor\\laravel\\framework\\src\\Illuminate\\Routing\\AbstractRouteCollection.php",
            "line": 41,
            "function": "getRouteForMethods",
            "class": "Illuminate\\Routing\\AbstractRouteCollection",
            "type": "->"
        },
        {
            "file": "C:\\phpstudy_pro\\WWW\\api.demo.test\\vendor\\laravel\\framework\\src\\Illuminate\\Routing\\RouteCollection.php",
            "line": 162,
            "function": "handleMatchedRoute",
            "class": "Illuminate\\Routing\\AbstractRouteCollection",
            "type": "->"
        },
        {
            "file": "C:\\phpstudy_pro\\WWW\\api.demo.test\\vendor\\laravel\\framework\\src\\Illuminate\\Routing\\Router.php",
            "line": 761,
            "function": "match",
            "class": "Illuminate\\Routing\\RouteCollection",
            "type": "->"
        },
        {
            "file": "C:\\phpstudy_pro\\WWW\\api.demo.test\\vendor\\laravel\\framework\\src\\Illuminate\\Routing\\Router.php",
            "line": 748,
            "function": "findRoute",
            "class": "Illuminate\\Routing\\Router",
            "type": "->"
        },
        {
            "file": "C:\\phpstudy_pro\\WWW\\api.demo.test\\vendor\\laravel\\framework\\src\\Illuminate\\Routing\\Router.php",
            "line": 737,
            "function": "dispatchToRoute",
            "class": "Illuminate\\Routing\\Router",
            "type": "->"
        },
        {
            "file": "C:\\phpstudy_pro\\WWW\\api.demo.test\\vendor\\laravel\\framework\\src\\Illuminate\\Foundation\\Http\\Kernel.php",
            "line": 200,
            "function": "dispatch",
            "class": "Illuminate\\Routing\\Router",
            "type": "->"
        },
        {
            "file": "C:\\phpstudy_pro\\WWW\\api.demo.test\\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php",
            "line": 144,
            "function": "Illuminate\\Foundation\\Http\\{closure}",
            "class": "Illuminate\\Foundation\\Http\\Kernel",
            "type": "->"
        },
        {
            "file": "C:\\phpstudy_pro\\WWW\\api.demo.test\\vendor\\laravel\\framework\\src\\Illuminate\\Foundation\\Http\\Middleware\\TransformsRequest.php",
            "line": 21,
            "function": "Illuminate\\Pipeline\\{closure}",
            "class": "Illuminate\\Pipeline\\Pipeline",
            "type": "->"
        },
        {
            "file": "C:\\phpstudy_pro\\WWW\\api.demo.test\\vendor\\laravel\\framework\\src\\Illuminate\\Foundation\\Http\\Middleware\\ConvertEmptyStringsToNull.php",
            "line": 31,
            "function": "handle",
            "class": "Illuminate\\Foundation\\Http\\Middleware\\TransformsRequest",
            "type": "->"
        },
        {
            "file": "C:\\phpstudy_pro\\WWW\\api.demo.test\\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php",
            "line": 183,
            "function": "handle",
            "class": "Illuminate\\Foundation\\Http\\Middleware\\ConvertEmptyStringsToNull",
            "type": "->"
        },
        {
            "file": "C:\\phpstudy_pro\\WWW\\api.demo.test\\vendor\\laravel\\framework\\src\\Illuminate\\Foundation\\Http\\Middleware\\TransformsRequest.php",
            "line": 21,
            "function": "Illuminate\\Pipeline\\{closure}",
            "class": "Illuminate\\Pipeline\\Pipeline",
            "type": "->"
        },
        {
            "file": "C:\\phpstudy_pro\\WWW\\api.demo.test\\vendor\\laravel\\framework\\src\\Illuminate\\Foundation\\Http\\Middleware\\TrimStrings.php",
            "line": 40,
            "function": "handle",
            "class": "Illuminate\\Foundation\\Http\\Middleware\\TransformsRequest",
            "type": "->"
        },
        {
            "file": "C:\\phpstudy_pro\\WWW\\api.demo.test\\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php",
            "line": 183,
            "function": "handle",
            "class": "Illuminate\\Foundation\\Http\\Middleware\\TrimStrings",
            "type": "->"
        },
        {
            "file": "C:\\phpstudy_pro\\WWW\\api.demo.test\\vendor\\laravel\\framework\\src\\Illuminate\\Foundation\\Http\\Middleware\\ValidatePostSize.php",
            "line": 27,
            "function": "Illuminate\\Pipeline\\{closure}",
            "class": "Illuminate\\Pipeline\\Pipeline",
            "type": "->"
        },
        {
            "file": "C:\\phpstudy_pro\\WWW\\api.demo.test\\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php",
            "line": 183,
            "function": "handle",
            "class": "Illuminate\\Foundation\\Http\\Middleware\\ValidatePostSize",
            "type": "->"
        },
        {
            "file": "C:\\phpstudy_pro\\WWW\\api.demo.test\\vendor\\laravel\\framework\\src\\Illuminate\\Foundation\\Http\\Middleware\\PreventRequestsDuringMaintenance.php",
            "line": 99,
            "function": "Illuminate\\Pipeline\\{closure}",
            "class": "Illuminate\\Pipeline\\Pipeline",
            "type": "->"
        },
        {
            "file": "C:\\phpstudy_pro\\WWW\\api.demo.test\\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php",
            "line": 183,
            "function": "handle",
            "class": "Illuminate\\Foundation\\Http\\Middleware\\PreventRequestsDuringMaintenance",
            "type": "->"
        },
        {
            "file": "C:\\phpstudy_pro\\WWW\\api.demo.test\\vendor\\laravel\\framework\\src\\Illuminate\\Http\\Middleware\\HandleCors.php",
            "line": 62,
            "function": "Illuminate\\Pipeline\\{closure}",
            "class": "Illuminate\\Pipeline\\Pipeline",
            "type": "->"
        },
        {
            "file": "C:\\phpstudy_pro\\WWW\\api.demo.test\\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php",
            "line": 183,
            "function": "handle",
            "class": "Illuminate\\Http\\Middleware\\HandleCors",
            "type": "->"
        },
        {
            "file": "C:\\phpstudy_pro\\WWW\\api.demo.test\\vendor\\laravel\\framework\\src\\Illuminate\\Http\\Middleware\\TrustProxies.php",
            "line": 39,
            "function": "Illuminate\\Pipeline\\{closure}",
            "class": "Illuminate\\Pipeline\\Pipeline",
            "type": "->"
        },
        {
            "file": "C:\\phpstudy_pro\\WWW\\api.demo.test\\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php",
            "line": 183,
            "function": "handle",
            "class": "Illuminate\\Http\\Middleware\\TrustProxies",
            "type": "->"
        },
        {
            "file": "C:\\phpstudy_pro\\WWW\\api.demo.test\\vendor\\laravel\\framework\\src\\Illuminate\\Pipeline\\Pipeline.php",
            "line": 119,
            "function": "Illuminate\\Pipeline\\{closure}",
            "class": "Illuminate\\Pipeline\\Pipeline",
            "type": "->"
        },
        {
            "file": "C:\\phpstudy_pro\\WWW\\api.demo.test\\vendor\\laravel\\framework\\src\\Illuminate\\Foundation\\Http\\Kernel.php",
            "line": 175,
            "function": "then",
            "class": "Illuminate\\Pipeline\\Pipeline",
            "type": "->"
        },
        {
            "file": "C:\\phpstudy_pro\\WWW\\api.demo.test\\vendor\\laravel\\framework\\src\\Illuminate\\Foundation\\Http\\Kernel.php",
            "line": 144,
            "function": "sendRequestThroughRouter",
            "class": "Illuminate\\Foundation\\Http\\Kernel",
            "type": "->"
        },
        {
            "file": "C:\\phpstudy_pro\\WWW\\api.demo.test\\public\\index.php",
            "line": 51,
            "function": "handle",
            "class": "Illuminate\\Foundation\\Http\\Kernel",
            "type": "->"
        }
    ]
}

三、實(shí)現(xiàn)處理

  • 添加異常處理類
PS C:\phpstudy_pro\WWW\api.demo.test> php artisan make:exception ApiException

   INFO  Exception [C:\phpstudy_pro\WWW\api.demo.test\app\Exceptions\ApiException.php] created successfully.  

修改ApiException.php

<?php

namespace App\Exceptions;

use Exception;

class ApiException extends Exception
{
    public function __construct(string $message = "", int $code = 422, ?Throwable $previous = null)
    {
        parent::__construct($message, $code, $previous);
    }

    public function render()
    {
        return response()->json([
            'code' => $this->code ?? 422,
            'message' => $this->message,
        ]);
    }
}

測試效果:


image.png
{
    "code": 422,
    "message": "用戶名或密碼不正確!"
}
image.png

一般情況卷要,都可以使用throw new ApiException('用戶名或密碼不正確!');的形式進(jìn)行處理渣聚。但是系統(tǒng)的ValidationException就不能處理了,那么能不能統(tǒng)一一下呢僧叉?當(dāng)然可以奕枝。重寫 public function render($request, Throwable $e)即可。

<?php

namespace App\Exceptions;

use Illuminate\Auth\AuthenticationException;
use Illuminate\Contracts\Support\Responsable;
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
use Illuminate\Http\Exceptions\HttpResponseException;
use Illuminate\Routing\Router;
use Illuminate\Validation\ValidationException;
use Throwable;

class Handler extends ExceptionHandler
{
    /**
     * The list of the inputs that are never flashed to the session on validation exceptions.
     *
     * @var array<int, string>
     */
    protected $dontFlash = [
        'current_password',
        'password',
        'password_confirmation',
    ];

    /**
     * Register the exception handling callbacks for the application.
     */
    public function register(): void
    {
        $this->reportable(function (Throwable $e) {
            //
        });
    }

    /**
     * 重寫render實(shí)現(xiàn)
     * @param $request
     * @param Throwable $e
     * @return \Illuminate\Http\JsonResponse|\Illuminate\Http\RedirectResponse|\Illuminate\Http\Response|mixed|\Symfony\Component\HttpFoundation\Response
     * @throws ApiException
     * @throws \ReflectionException
     */
    public function render($request, Throwable $e)
    {
        if ($e instanceof ValidationException) {// 處理驗(yàn)證異常信息
            throw new ApiException($e->getMessage(), $e->status);
        } else {// 保持原來的實(shí)現(xiàn)
            $e = $this->mapException($e);

            if (method_exists($e, 'render') && $response = $e->render($request)) {
                return Router::toResponse($request, $response);
            }

            if ($e instanceof Responsable) {
                return $e->toResponse($request);
            }

            $e = $this->prepareException($e);

            if ($response = $this->renderViaCallbacks($request, $e)) {
                return $response;
            }

            return match (true) {
                $e instanceof HttpResponseException => $e->getResponse(),
                $e instanceof AuthenticationException => $this->unauthenticated($request, $e),
                $e instanceof ValidationException => $this->convertValidationExceptionToResponse($e, $request),
                default => $this->renderExceptionResponse($request, $e),
            };
        }
    }
}

實(shí)現(xiàn)效果:
有處理部分


image.png

不處理部分:


image.png
  • 總結(jié):

1.新增ApiException.php

php artisan make:exception ApiException

并實(shí)現(xiàn)如下:

<?php

namespace App\Exceptions;

use Exception;

class ApiException extends Exception
{
    public function __construct(string $message = "", int $code = 422, ?Throwable $previous = null)
    {
        parent::__construct($message, $code, $previous);
    }

    public function render()
    {
        return response()->json([
            'code' => $this->code ?? 422,
            'message' => $this->message,
        ]);
    }
}

2.修改Exceptions 下的Handler.php

 /**
     * 重寫render實(shí)現(xiàn)
     * @param $request
     * @param Throwable $e
     * @return \Illuminate\Http\JsonResponse|\Illuminate\Http\RedirectResponse|\Illuminate\Http\Response|mixed|\Symfony\Component\HttpFoundation\Response
     * @throws ApiException
     * @throws \ReflectionException
     */
    public function render($request, Throwable $e)
    {
        if ($request->ajax() && $e instanceof ValidationException) {// 處理驗(yàn)證異常信息
            throw new ApiException($e->getMessage(), $e->status);
        } else {// 保持原來的實(shí)現(xiàn)
            $e = $this->mapException($e);

            if (method_exists($e, 'render') && $response = $e->render($request)) {
                return Router::toResponse($request, $response);
            }

            if ($e instanceof Responsable) {
                return $e->toResponse($request);
            }

            $e = $this->prepareException($e);

            if ($response = $this->renderViaCallbacks($request, $e)) {
                return $response;
            }

            return match (true) {
                $e instanceof HttpResponseException => $e->getResponse(),
                $e instanceof AuthenticationException => $this->unauthenticated($request, $e),
                $e instanceof ValidationException => $this->convertValidationExceptionToResponse($e, $request),
                default => $this->renderExceptionResponse($request, $e),
            };
        }
    }

至此瓶堕,處理完畢隘道。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市郎笆,隨后出現(xiàn)的幾起案子谭梗,更是在濱河造成了極大的恐慌,老刑警劉巖宛蚓,帶你破解...
    沈念sama閱讀 216,402評(píng)論 6 499
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件激捏,死亡現(xiàn)場離奇詭異,居然都是意外死亡苍息,警方通過查閱死者的電腦和手機(jī)缩幸,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,377評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來竞思,“玉大人表谊,你說我怎么就攤上這事「桥纾” “怎么了爆办?”我有些...
    開封第一講書人閱讀 162,483評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長课梳。 經(jīng)常有香客問我距辆,道長余佃,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,165評(píng)論 1 292
  • 正文 為了忘掉前任跨算,我火速辦了婚禮爆土,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘诸蚕。我一直安慰自己步势,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,176評(píng)論 6 388
  • 文/花漫 我一把揭開白布背犯。 她就那樣靜靜地躺著坏瘩,像睡著了一般。 火紅的嫁衣襯著肌膚如雪漠魏。 梳的紋絲不亂的頭發(fā)上倔矾,一...
    開封第一講書人閱讀 51,146評(píng)論 1 297
  • 那天,我揣著相機(jī)與錄音柱锹,去河邊找鬼哪自。 笑死,一個(gè)胖子當(dāng)著我的面吹牛禁熏,可吹牛的內(nèi)容都是我干的提陶。 我是一名探鬼主播,決...
    沈念sama閱讀 40,032評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼匹层,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了锌蓄?” 一聲冷哼從身側(cè)響起升筏,我...
    開封第一講書人閱讀 38,896評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎瘸爽,沒想到半個(gè)月后您访,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,311評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡剪决,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,536評(píng)論 2 332
  • 正文 我和宋清朗相戀三年灵汪,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片柑潦。...
    茶點(diǎn)故事閱讀 39,696評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡享言,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出渗鬼,到底是詐尸還是另有隱情览露,我是刑警寧澤,帶...
    沈念sama閱讀 35,413評(píng)論 5 343
  • 正文 年R本政府宣布譬胎,位于F島的核電站差牛,受9級(jí)特大地震影響命锄,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜偏化,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,008評(píng)論 3 325
  • 文/蒙蒙 一脐恩、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧侦讨,春花似錦驶冒、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至怜俐,卻和暖如春身堡,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背拍鲤。 一陣腳步聲響...
    開封第一講書人閱讀 32,815評(píng)論 1 269
  • 我被黑心中介騙來泰國打工贴谎, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人季稳。 一個(gè)月前我還...
    沈念sama閱讀 47,698評(píng)論 2 368
  • 正文 我出身青樓擅这,卻偏偏與公主長得像,于是被迫代替她去往敵國和親景鼠。 傳聞我的和親對(duì)象是個(gè)殘疾皇子仲翎,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,592評(píng)論 2 353

推薦閱讀更多精彩內(nèi)容