我应该在 Laravel 中嵌套资源的路由吗?
Posted
技术标签:
【中文标题】我应该在 Laravel 中嵌套资源的路由吗?【英文标题】:Should I Nest Routes to Resources in Laravel? 【发布时间】:2019-04-20 20:56:33 【问题描述】:这可能有点主观,但我觉得必须存在最佳实践(甚至对于 Laravel 应用程序来说也是好的设计)。谷歌搜索会导致很多与这个问题的实际点无关的事情。
假设我正在构建一个包含团队的 Web 应用程序,其中可能有项目,可能有文档。
我应该设计路由以使文档在他们所属的项目的路径内,然后在他们所属的团队的路径内,还是保持在顶层?
据我所知,这个频谱有两个端点值得讨论(其他选项只是介于两者之间的灰色):
嵌套
例如,文档 C 位于:/teams/team-a/projects/project-b/documents/doc-c
这样做很容易,在路由文件中,我可以使用route groups 来帮助保持结构清洁。我认为这对用户来说更合乎逻辑,也许更方便(他们可以自己制定 URL!)。我担心的是我将复杂性导入每个页面请求:
检查路由是否完整(即 doc-c 确实属于 project-b),并且 用户有权访问整个路由中的每个嵌套资产。我是否应该在每个控制器方法的开头,为每个路由参数对每个资源进行门/策略检查?否则,这能抽象到哪里去?
关于路由完整性,我从未见过对此进行测试的示例 - 所以这不是一种常见的方法吗?如果我们不验证路由完整性,那么页面可能会通过入侵路由来显示混合信息(例如,/teams/team-a/projects/project-Z/documents/doc-c,会在 doc 上显示有关项目 Z 的信息-c 的页面)。
无嵌套
例如,Doc C 位于:/documents/doc-c
在这个例子中,每个资产都有自己的基础路由,我猜更像是一个 API。
不需要完整性检查,控制器会预先确定显示的其他资产以生成视图。
但是这个用户体验足够好吗?我见过的大多数网站都没有这样做。
【问题讨论】:
值得注意的是,Laravel 7 现在开箱即用,如果您使用此处描述的路由范围:laravel.com/docs/7.x/routing#implicit-binding 【参考方案1】:我认为你应该利用角色概念、路由分组、中间件概念来构建这个应用程序
对于角色相关的事情检查https://github.com/spatie/laravel-permission,一个很好的包
例如:
Route::group(['middleware' => ['role:super-admin']], function ()
//
);
你几乎可以使用上面的包做任何角色,权限相关的事情。
担任项目经理、开发人员等角色。
根据角色对路由进行分组并根据需要分配适当的中间件。
对于网址:/teams/team-a/projects/project-b/documents/doc-c
检查 Authenticated 用户在 team-a 和 project-b 中是否有角色(您可以根据需要在中间件、控制器或自定义服务提供者中进行检查)。
也只需检查https://laravelvoyager.com/
谢谢
【讨论】:
这家伙没有提到任何关于角色或身份验证的事情,都是关于嵌套路由的。 @YamenAshraf 如果你能注意,你会在我的回答中得到“路线分组”。 路由分组是苹果,路由嵌套是橙色。 @YamenAshraf Bro 然后定义您的路由嵌套,呵呵,他想要一些具有公共前缀的路由,他的问题是(路由访问完整性)。基本上他需要一个简单的架构来遵循。不要让事情变得复杂。【参考方案2】:这一直是开发者之间的讨论,即使是 Laravel 的创建者也有这种说法,那么有什么好的做法呢?
最近我看到 Taylor Otwell 的一条推文说他已弃用
Laravel 文档中的嵌套路由部分,因为他不喜欢它,而当您打开 Laracasts 时,您会看到 Jeffrey 正在实现这个概念,例如 /series/php-testing/episodes/hello-world
。
正如我所说,这是一个安静的争论,当涉及到这样的选择时,我总是做对我感觉好的事情。所以如果我是你,我不会嵌套teams
或projects
,但也许我会嵌套projects/documents
,我不会总是嵌套。
【讨论】:
【参考方案3】:这是一个有趣的问题 - 正如您所提到的,它可能有点主观,但值得讨论。
你提到了几点,所以我将尝试分别解决它们。
嵌套与不嵌套
在我看来,首先要弄清楚的是浏览器路由与 API 路由。如果您要提供 API - 无论是在您的应用内部还是在外部向公众提供 API,出于以下几个原因,我会避免嵌套路由:
resource/id 格式对于 API 来说非常标准且富有表现力 这使得记录更容易 这使得消费者应用更容易动态构建 API 请求但是,您的问题似乎确实集中在浏览器路由上。在我看来,浏览器路由可以而且应该是任何读起来很好的东西 - url,尤其是现在,可以被视为 UI 的一部分。例如,您可以从设置页面转到设置(我希望看到/settings
),如果我要进入进入通知设置部分,我希望看到@987654324 @。
路线在 UX 方面发挥作用并提供帮助 - 它们几乎是面包屑,应该看起来像这样。
所以,我肯定会嵌套浏览器路由,绝对不会嵌套 API。
路由完整性
我认为您问题的真正核心是关于路线完整性。我认为无论您是否选择嵌套,您都需要在假设有人篡改 url 的情况下检查您的权限 - 就像您假设用户篡改了表单输入一样。
基本上,您的路线(是否嵌套)充当输入,您需要对其进行验证。路由级中间件是一种方法,但通常过于通用而无法解决任何复杂的问题,因此您可能会发现使用控制器中间件更容易解决它 (https://laravel.com/docs/5.7/controllers#controller-middleware)。
所以你可以这样做:
public function __construct()
$this->middleware('auth');
$this->middleware('canViewProject')->only('show');
$this->middleware('canEditProject')->except('store');
您是使用 Gates、Policies 还是仅仅使用普通的旧中间件可能取决于项目的复杂性 - 但无论如何以上都适用 - 将 url 作为输入并进行相应的验证
【讨论】:
很好的答案。关于将“路由完整性”部分抽象到何处的任何观点?是否每个控制器方法都需要从检查 doc-x 是否属于 project-y 是否属于 team-z 开始?你以前见过这样的例子吗? 这真的取决于复杂性。听起来你正在做的事情相当复杂(也许?) - 所以政策可能会有所帮助(laravel.com/docs/5.7/authorization#creating-policies)。就示例而言,您的意思是像在开源项目中一样吗?没那么多,但我们根据项目混合使用中间件、门和策略。 因此,与其检查链接是否正常(部件嵌套是否正确),您认为哲学应该是测试用户是否可以访问每个部件?我认为 URL 检查的一部分返回 403,而另一部分返回 404?这不是两个独立的测试吗? 是的,这正是我会做的 - 根据答案,您的 url 本质上只是输入,如果它本身包含数据,则应该对其进行验证。然后,您可以根据需要创建响应代码【参考方案4】:这可能与最初的问题有些偏差,但我觉得 Adam Wathan 的 Laracon US 2017 演讲可能是此讨论的有用资源。 (https://www.youtube.com/watch?v=MF0jFKvS4SI)
我对开发比较陌生,因此在 github 上探索了很多代码库。我总是很难理解嵌套路线。因此,作为最佳实践,我更喜欢不嵌套而不是嵌套。
保持亚当所说的“CRUDY by design”,你实现的一件事,imo,就是路线名称的简化。如果我要在一个小组中工作,那是我更喜欢的模式。
但是,参考您问题的倒数第二段,我很难理解为什么不需要完整性检查。
【讨论】:
【参考方案5】:去年我一直在研究并改进它,最近偶然发现了一个优雅的解决方案。嵌套可以很好地完成,我坚持使用资源控制器和嵌套路由资源的点语法。
为了强制执行路由验证,我使用了类似下面的东西。有一个很好的答案here,它建议显式绑定有问题的模型。
所以
Route::resource('posts.comments', 'CommentsController');
你会使用
Route::bind('comment', function ($comment, $route)
return Comment::where('post_id', $route->parameter('post'))->findOrFail($comment);
);
这将自动在任何地方工作。如果需要,您可以测试要找到的上游参数,并仅在这些情况下执行测试(例如,满足仅指定注释的路由)。
随后在 Laravel 上做了很多工作。我对此的默认响应是“是的,使用路由嵌套”。我让路由保持安静,尽可能使用(嵌套)资源控制器,并为奇怪的用例使用单动作控制器。路由范围现在甚至是自动的,if specified correctly。
【讨论】:
以上是关于我应该在 Laravel 中嵌套资源的路由吗?的主要内容,如果未能解决你的问题,请参考以下文章