分享一種Laravel異常上下文解決方案

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?

下面由laravel教程欄目給大家介紹一種Laravel異常上下文解決方案,希望對需要的朋友有所幫助!

最近項目遇到一個情況,我們在遇到用戶訪問某個信息沒有權限的時候,希望提示詳細的原因,比如當訪問一個團隊資源時非成員訪問的場景下會提示一個:您不是 [xxxxxx] 團隊的成員,暫時無法查看,可,同時需要顯示打碼后的團隊名稱,以及加入按鈕,可是接口方的邏輯是當沒有權限時直接 abort 了:

abort_if(!$user->isMember($resouce->team), 403, '您無權訪問該資源');

得到的響應結果如下:

HTTP/1.0?403?Forbidden{ ????"message":?"您無權訪問該資源"}

我們不可能將 message 用 html 來完成前端提示頁的展示,這樣耦合性太強,違背了前后端分離的原則。我們的目標是返回如下的格式即可解決:

HTTP/1.0 403 Forbidden{     "message": "您無權訪問該資源",     "team": {         "id": "abxT8sioa0Ms",         "name": "CoDesign****"     }}

通過攜帶上下文的方法傳遞數據,方便了前端同學自由組合。

開始改造

當然這并不是什么復雜的事情,直接修改原來的 abort_if 即可解決:

-?abort_if(!$user->isMember($resouce->team),?403,?'您無權訪問該資源'); +?if?(!$user->isMember($resouce->team))?{ +????return?response()->json([ +????????'message'?=>?'您無權訪問該資源', +????????'team'?=>?[ +????????????'id'?=>?$resouce->team_id, +????????????'name'=>?$resouce->team->desensitised_name, +????????] +????],?403); +?}

這樣看起來解決了問題,可是試想一下,如果是在閉包里面檢測到異常想要退出,上面這種 return 式的寫法就會比較難搞了,畢竟 return 只會終止最近的上下文環境,我們還是希望像 abort 一樣能終止整個應用的執行,再進行另一番改造。

優化實現

看了 abort 源碼,我發現它的第一個參數其實支持 symfonyComponentHttpFoundationResponse 實例,而上面我們 return 的結果就是它的實例,所以我們只需要改成這樣就可以了:

?if?(!$user->isMember($resouce->team))?{ ????abort(response()->json([ ????????'message'?=>?'您無權訪問該資源', ????????'team'?=>?[ ????????????'id'?=>?$resouce->team_id, ????????????'name'=>?$resouce->team->desensitised_name, ????????] ????],?403)); ?}

看起來實現了異常中斷,可是新的問題來了,如果需要復用的時候還是比較尷尬,這段代碼將會重復出現在各種有此權限判斷的地方,這并不是我們想要的。

邏輯復用

為了達到邏輯復用,看了 AppExceptionsHandler 的實現,發現父類的 render 方法還有這么一個設計:

public?function?render($request,?Throwable?$e) { ????if?(method_exists($e,?'render')?&&?$response?=?$e->render($request))?{ ????????return?Router::toResponse($request,?$response); ????}?elseif?($e?instanceof?Responsable)?{ ????????return?$e->toResponse($request); ????} ????//...

所以,我們可以將這個邏輯抽離為一個獨立的異常類,實現 render 方法即可:

我們先創建一個異常類:

$?./artisan?make:exception?NotTeamMemberException

實現代碼如下:

<?php namespace AppExceptions; use AppTeam; class NotTeamMemberException extends Exception {     public Team $team;     public function __construct(Team $team, $message = "")     {         $this->team?=?$team; ????????parent::__construct($message,?403); ????} ????public?function?render() ????{ ????????return?response()-&gt;json( ????????????[ ????????????????'message'?=&gt;?!empty($this-&gt;message)???$this-&gt;message?:?'您無權訪問該資源', ????????????????'team'?=&gt;?[ ????????????????????'id'?=&gt;?$this-&gt;team-&gt;id, ????????????????????'name'?=&gt;?$this-&gt;team-&gt;desensitised_name, ????????????????], ????????????], ????????????403 ????????); ????} }

這樣一來,我們的邏輯就變成了:

if?(!$user-&gt;isMember($resouce-&gt;team))?{ ?????throw?new?NotTeamMemberException($resouce-&gt;team,?'您無權訪問該資源'); }

當然也可以簡寫為:

throw_if(!$user-&gt;isMember($resouce-&gt;team),?NotTeamMemberException::class,?$resouce-&gt;team,?'您無權訪問該資源');

問題到這里總算以一個比較完美的方式解決了,如果你有更好的方案歡迎評論探討。

? 版權聲明
THE END
喜歡就支持一下吧
點贊9 分享