我应该在 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

正如我所说,这是一个安静的争论,当涉及到这样的选择时,我总是做对我感觉好的事情。所以如果我是你,我不会嵌套teamsprojects,但也许我会嵌套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 中嵌套资源的路由吗?的主要内容,如果未能解决你的问题,请参考以下文章

Laravel 7 - 嵌套资源路由中的范围问题

带有前缀和资源的laravel嵌套路由不起作用

ExpressJS - 嵌套/链接资源的 RESTful 路由设计

在 /api/ 路由中嵌套资源路由

在 Laravel 中为资源控制器添加新方法

在 Laravel 路由上设置协议