최근 프로젝트에서 특정 정보에 대한 액세스 권한이 없는 사용자를 만나면 자세한 이유를 알려주시기 바랍니다. 예를 들어 팀 리소스에 액세스하고 비회원이 액세스하는 경우 메시지가 표시됩니다. 당신이 [xxxxxx] 팀의 구성원이 아니면 일시적으로 볼 수 없습니다. 동시에 <가입 신청>
을 할 수 있습니다. 참여 버튼이 표시되어야 합니다. 그러나 인터페이스의 논리는 직접 중단하는 것입니다. abort_if(!$user->isMember($resouce->team), 403, '您无权访问该资源');
HTTP/1.0 403 Forbidden{
"message": "您无权访问该资源"}
HTTP/1.0 403 Forbidden{ "message": "您无权访问该资源", "team": { "id": "abxT8sioa0Ms", "name": "CoDesign****" }}
프론트 엔드 학생들의 자유로운 조합을 촉진하는 컨텍스트를 전달하여 데이터를 전송합니다. 您不是 [xxxxxx] 团队的成员,暂时无法查看,可<申请加入>
,同时需要显示打码后的团队名称,以及加入按钮,可是接口方的逻辑是当没有权限时直接 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
변환 시작
물론 이것은 복잡한 문제가 아닙니다. 원래abort_if
를 수정하여 해결하세요. it :\throw_if(!$user->isMember($resouce->team), NotTeamMemberException::class, $resouce->team, '您无权访问该资源');
return
작성 스타일이 결국 더 어려워질 것이라고 상상해 보세요. return
은 최신 컨텍스트만 종료합니다. 우리는 여전히 abort
와 같이 전체 애플리케이션의 실행을 종료한 다음 또 다른 변환을 수행하기를 희망합니다.
최적화된 구현
🎜🎜abort
의 소스 코드를 살펴본 후 첫 번째 매개변수가 실제로 지원하는 것을 발견했습니다. SymfonyComponentHttpFoundationResponse
인스턴스이고 위의 return
결과가 해당 인스턴스이므로 다음으로만 변경하면 됩니다. 🎜rrreee🎜예외 인터럽트가 구현된 것 같지만, 새로운 문제는 재사용해야 한다면 여전히 당황스럽다는 것입니다. 이 코드는 이 권한이 판단되는 여러 위치에서 반복적으로 나타납니다. 🎜🎜🎜로직 재사용🎜🎜🎜로직 재사용을 달성하기 위해 AppExceptionsHandler
구현을 살펴본 결과 상위 클래스의 render
메서드가 이러한 디자인을 가지고 있음을 발견했습니다. : 🎜rrreee🎜 따라서 이 로직을 독립적인 예외 클래스로 추출하고 render
메서드를 구현할 수 있습니다. 🎜🎜먼저 예외 클래스를 만듭니다. 🎜rrreee🎜구현 코드는 다음과 같습니다. 🎜rrreee 🎜이렇게 하면 우리의 논리는 다음과 같이 됩니다. 🎜rrreee🎜물론 다음과 같이 축약할 수도 있습니다. 🎜rrreee🎜 문제는 마침내 비교적 완벽한 방법으로 해결되었습니다. 더 나은 해결책이 있다면 댓글을 달고 토론해 주세요. 🎜위 내용은 Laravel 예외 컨텍스트 솔루션 공유의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!