Laravel異常上下文解決方案分享
前言
異常時(shí)我們通常希望在用戶側(cè)給一個(gè)友好的提示,但默認(rèn)使用框架的異常處理方案是不 OK 的。
最近項(xiàng)目遇到一個(gè)情況,我們在遇到用戶訪問某個(gè)信息沒有權(quán)限的時(shí)候,希望提示詳細(xì)的原因,比如當(dāng)訪問一個(gè)團(tuán)隊(duì)資源時(shí)非成員訪問的場景下會(huì)提示一個(gè):您不是 [xxxxxx] 團(tuán)隊(duì)的成員,暫時(shí)無法查看,可<申請加入>,同時(shí)需要顯示打碼后的團(tuán)隊(duì)名稱,以及加入按鈕,可是接口方的邏輯是當(dāng)沒有權(quán)限時(shí)直接 abort 了:
abort_if(!$user->isMember($resouce->team), 403, '您無權(quán)訪問該資源');
得到的響應(yīng)結(jié)果如下:
HTTP/1.0 403 Forbidden { "message": "您無權(quán)訪問該資源" }
我們不可能將 message 用 html 來完成前端提示頁的展示,這樣耦合性太強(qiáng),違背了前后端分離的原則。我們的目標(biāo)是返回如下的格式即可解決:
HTTP/1.0 403 Forbidden { "message": "您無權(quán)訪問該資源", "team": { "id": "abxT8sioa0Ms", "name": "CoDesign****" } }
通過攜帶上下文的方法傳遞數(shù)據(jù),方便了前端同學(xué)自由組合。
開始改造
當(dāng)然這并不是什么復(fù)雜的事情,直接修改原來的 abort_if 即可解決:
- abort_if(!$user->isMember($resouce->team), 403, '您無權(quán)訪問該資源'); + if (!$user->isMember($resouce->team)) { + return response()->json([ + 'message' => '您無權(quán)訪問該資源', + 'team' => [ + 'id' => $resouce->team_id, + 'name'=> $resouce->team->desensitised_name, + ] + ], 403); + }
這樣看起來解決了問題,可是試想一下,如果是在閉包里面檢測到異常想要退出,上面這種 return 式的寫法就會(huì)比較難搞了,畢竟 return 只會(huì)終止最近的上下文環(huán)境,我們還是希望像 abort 一樣能終止整個(gè)應(yīng)用的執(zhí)行,再進(jìn)行另一番改造。
優(yōu)化實(shí)現(xiàn)
看了 abort 源碼,我發(fā)現(xiàn)它的第一個(gè)參數(shù)其實(shí)支持 \Symfony\Component\HttpFoundation\Response 實(shí)例,而上面??我們 return 的結(jié)果就是它的實(shí)例,所以我們只需要改成這樣就可以了:
if (!$user->isMember($resouce->team)) { abort(response()->json([ 'message' => '您無權(quán)訪問該資源', 'team' => [ 'id' => $resouce->team_id, 'name'=> $resouce->team->desensitised_name, ] ], 403)); }
新的問題來了,如果需要復(fù)用的時(shí)候還是比較尷尬,這段代碼將會(huì)重復(fù)出現(xiàn)在各種有此權(quán)限判斷的地方,這并不是我們想要的。
邏輯復(fù)用
為了達(dá)到邏輯復(fù)用,我認(rèn)證看了 \App\Exceptions\Handler 的實(shí)現(xiàn),發(fā)現(xiàn)父類的 render 方法還有這么一個(gè)設(shè)計(jì):
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); } //...
所以,我們可以將這個(gè)邏輯抽離為一個(gè)獨(dú)立的異常類,實(shí)現(xiàn) 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 : '您無權(quán)訪問該資源', 'team' => [ 'id' => $this->team->id, 'name' => $this->team->desensitised_name, ], ], 403 ); } }
這樣一來,我們的邏輯就變成了:
if (!$user->isMember($resouce->team)) { throw new NotTeamMemberException($resouce->team, '您無權(quán)訪問該資源'); }
當(dāng)然也可以簡化為:
\throw_if(!$user->isMember($resouce->team), NotTeamMemberException::class, $resouce->team, '您無權(quán)訪問該資源');
問題到這里總算以一個(gè)比較完美的方式解決了,如果你有更好的方案歡迎評論探討。
總結(jié)
到此這篇關(guān)于Laravel異常上下文解決方案的文章就介紹到這了,更多相關(guān)Laravel異常上下文解決內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
php 如何禁用eval() 函數(shù)實(shí)例詳解
在php中eval是一個(gè)函數(shù)并且不能直接禁用了,但eval函數(shù)又相當(dāng)?shù)奈kU(xiǎn)并經(jīng)常會(huì)出現(xiàn)一些問題,今天我們就一起來看看eval函數(shù)對數(shù)組的操作及php 如何禁用eval() 函數(shù),需要的朋友可以參考下2016-12-12ThinkPHP模板循環(huán)輸出Volist標(biāo)簽用法實(shí)例詳解
這篇文章主要介紹了ThinkPHP模板循環(huán)輸出Volist標(biāo)簽用法,結(jié)合實(shí)例形式詳細(xì)分析了Volist標(biāo)簽的功能,使用方法與相關(guān)注意事項(xiàng),需要的朋友可以參考下2016-03-03Laravel框架定時(shí)任務(wù)2種實(shí)現(xiàn)方式示例
這篇文章主要介紹了Laravel框架定時(shí)任務(wù)2種實(shí)現(xiàn)方式,結(jié)合實(shí)例形式較為詳細(xì)的分析了Laravel框架定時(shí)任務(wù)相關(guān)實(shí)現(xiàn)方法及操作注意事項(xiàng),需要的朋友可以參考下2018-12-12采用ThinkPHP中F方法實(shí)現(xiàn)快速緩存實(shí)例
一般使用文件方式的緩存就能夠滿足要求,而thinkPHP還提供了一個(gè)專門用于文件方式的快速緩存方法F方法,需要的朋友可以參考下2014-06-06PHP 結(jié)合 Boostrap 結(jié)合 js 實(shí)現(xiàn)學(xué)生列表刪除編輯及搜索功能
這篇文章主要介紹了PHP 結(jié)合 Boostrap 結(jié)合 js 實(shí)現(xiàn)學(xué)生列表刪除編輯以及搜索功能,非常不錯(cuò),具有一定的參考借鑒價(jià)值 ,需要的朋友可以參考下2019-05-05