Heim >PHP-Framework >Laravel >Teilen Sie eine Laravel-Ausnahmekontextlösung
Kürzlich ist im Projekt eine Situation aufgetreten, wenn wir auf einen Benutzer stoßen, der keine Berechtigung zum Zugriff auf bestimmte Informationen hat. Wenn beispielsweise auf eine Teamressource zugegriffen wird und Nichtmitglieder darauf zugreifen, wird eine Meldung angezeigt wird aufgefordert: Wenn Sie kein Mitglied des [xxxxxx]-Teams sind, können Sie es vorübergehend nicht anzeigen. Sie können gleichzeitig den codierten Teamnamen und angeben Die Verknüpfungsschaltfläche muss jedoch angezeigt werden. Die Logik der Schnittstelle besteht jedoch darin, direkt <code>abzubrechen
: abort_if(!$user->isMember($resouce->team), 403, '您无权访问该资源');
Das Antwortergebnis lautet wie folgt: HTTP/1.0 403 Forbidden{
"message": "您无权访问该资源"}
Es ist für uns unmöglich, HTML zur Anzeige zu verwenden Die Front-End-Eingabeaufforderungsseite ist zu stark und verstößt gegen das Prinzip der Front-End- und Back-End-Trennung. Unser Ziel ist es, das folgende Format zurückzugeben, um das Problem zu lösen:
HTTP/1.0 403 Forbidden{ "message": "您无权访问该资源", "team": { "id": "abxT8sioa0Ms", "name": "CoDesign****" }}
Übertragen Sie Daten durch Übertragen des Kontexts, was die freie Kombination von Front-End-Studenten erleichtert. 您不是 [xxxxxx] 团队的成员,暂时无法查看,可589dbe80f52208edfac91e23a0ccf85e
,同时需要显示打码后的团队名称,以及加入按钮,可是接口方的逻辑是当没有权限时直接 abort
了:
- 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); + }
得到的响应结果如下:
if (!$user->isMember($resouce->team)) { abort(response()->json([ 'message' => '您无权访问该资源', 'team' => [ 'id' => $resouce->team_id, 'name'=> $resouce->team->desensitised_name, ] ], 403)); }
我们不可能将 message 用 html 来完成前端提示页的展示,这样耦合性太强,违背了前后端分离的原则。我们的目标是返回如下的格式即可解决:
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); } //...
通过携带上下文的方法传递数据,方便了前端同学自由组合。
开始改造
当然这并不是什么复杂的事情,直接修改原来的 abort_if
即可解决:
$ ./artisan make:exception NotTeamMemberException
这样看起来解决了问题,可是试想一下,如果是在闭包里面检测到异常想要退出,上面这种 return
式的写法就会比较难搞了,毕竟 return
只会终止最近的上下文环境,我们还是希望像 abort
一样能终止整个应用的执行,再进行另一番改造。
优化实现
看了 abort
源码,我发现它的第一个参数其实支持 SymfonyComponentHttpFoundationResponse
实例,而上面我们 return
的结果就是它的实例,所以我们只需要改成这样就可以了:
<?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 ); } }
看起来实现了异常中断,可是新的问题来了,如果需要复用的时候还是比较尴尬,这段代码将会重复出现在各种有此权限判断的地方,这并不是我们想要的。
逻辑复用
为了达到逻辑复用,看了 AppExceptionsHandler
的实现,发现父类的 render
方法还有这么一个设计:
if (!$user->isMember($resouce->team)) { throw new NotTeamMemberException($resouce->team, '您无权访问该资源'); }
所以,我们可以将这个逻辑抽离为一个独立的异常类,实现 render
Starten Sie die Transformation
Natürlich ist dies keine komplizierte Angelegenheit, ändern Sie einfach den ursprünglichenabort_if
, um ihn zu lösen it:\throw_if(!$user->isMember($resouce->team), NotTeamMemberException::class, $resouce->team, '您无权访问该资源');Das scheint das Problem zu lösen, aber stellen Sie sich vor, wenn beim Schließen eine Ausnahme erkannt wird und Sie beenden möchten, wird der obige
return
-Schreibstil schließlich schwieriger return beendet nur den neuesten Kontext. Wir hoffen immer noch, die Ausführung der gesamten Anwendung wie abort
zu beenden und dann eine weitere Transformation durchzuführen.
Optimierte Implementierung
🎜🎜Nachdem ich mir den Quellcode vonabort
angesehen hatte, stellte ich fest, dass der erste Parameter tatsächlich unterstützt wird SymfonyComponentHttpFoundationResponse
-Instanz, und das Ergebnis unserer return
oben ist ihre Instanz, daher müssen wir sie nur wie folgt ändern: 🎜rrreee🎜Es scheint, dass Ausnahme-Interrupts implementiert sind, aber Das neue Problem besteht darin, dass es immer noch peinlich ist, wenn dieser Code an verschiedenen Stellen angezeigt wird. Dies ist nicht das, was wir wollen. 🎜🎜🎜Logische Wiederverwendung🎜🎜🎜Um eine logische Wiederverwendung zu erreichen, habe ich mir die Implementierung von AppExceptionsHandler
angesehen und festgestellt, dass die render
-Methode der übergeordneten Klasse ein solches Design hat : 🎜rrreee🎜 Daher können wir diese Logik in eine unabhängige Ausnahmeklasse extrahieren und die Methode render
implementieren: 🎜🎜Wir erstellen zunächst eine Ausnahmeklasse: 🎜rrreee🎜Der Implementierungscode lautet wie folgt: 🎜rrreee 🎜Auf diese Weise wird unsere Logik zu: 🎜rrreee🎜Natürlich kann es auch abgekürzt werden als: 🎜rrreee🎜Das Problem ist endlich auf eine perfektere Weise gelöst, bitte kommentieren und diskutieren Sie es. 🎜Das obige ist der detaillierte Inhalt vonTeilen Sie eine Laravel-Ausnahmekontextlösung. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!