下上由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
原始碼,我發現它的第一個參數其實支援 \Symfony\Component\HttpFoundation\Response
實例,而上面我們return
的結果就是它的實例,所以我們只需要改成這樣就可以了:
if (!$user->isMember($resouce->team)) { abort(response()->json([ 'message' => '您无权访问该资源', 'team' => [ 'id' => $resouce->team_id, 'name'=> $resouce->team->desensitised_name, ] ], 403)); }
看起來實現了異常中斷,可是新的問題來了,如果需要復用的時候還是比較尷尬,這段程式碼將會重複出現在各種有此權限判斷的地方,這並不是我們想要的。
邏輯復用
為了達到邏輯復用,看了\App\Exceptions\Handler
的實現,發現父類別的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 App\Exceptions; use App\Team; 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()->json( [ 'message' => !empty($this->message) ? $this->message : '您无权访问该资源', 'team' => [ 'id' => $this->team->id, 'name' => $this->team->desensitised_name, ], ], 403 ); } }
這樣一來,我們的邏輯就變成了:
if (!$user->isMember($resouce->team)) { throw new NotTeamMemberException($resouce->team, '您无权访问该资源'); }
當然也可以簡寫為:
\throw_if(!$user->isMember($resouce->team), NotTeamMemberException::class, $resouce->team, '您无权访问该资源');
問題到這裡總算以一個比較完美的方式解決了,如果你有更好的方案歡迎評論探討。
以上是分享一個Laravel異常上下文解決方案的詳細內容。更多資訊請關注PHP中文網其他相關文章!