现代Web应用程序框架如何以及为什么演变为将URL路由与文件系统分离?


67

与大约10年前相比,我已经注意到使用路由样式将URL路径与文件系统分离的框架向框架的转变。这通常是在前控制器模式的帮助下完成的。

即,以前,URL路径直接映射到文件系统,因此反映了磁盘上的确切文件和文件夹,如今,实际的URL路径已编程为通过配置定向到特定的类,因此不再反映文件系统文件夹和文件结构。

这如何以及为什么变得司空见惯?它如何以及为什么决定将其“更好”到有效地摒弃了曾经司空见惯的直接归档方法?

其他答案

这里有一个类似的答案,有点涉及路由的概念以及一些优点和缺点:对于PHP框架,为什么要使用“路由”概念?

但是,它没有解决历史变化方面的问题,也没有解决这种变化如何或为什么逐渐发生的问题,到如今使用这种新的路由样式模式的新项目几乎都在哪里,而直接归档已经过时或被放弃了。

同样,提到的大多数优缺点似乎不足以保证进行这样的全球变更。我能看到推动这一变化的唯一好处也许是向最终用户隐藏了文件/文件夹系统,并且缺少?param=value&param2=value,这使得URL看起来有点干净。但是,这些是改变的唯一原因吗?如果是,为什么背后有这些原因?

例子:

我最熟悉PHP框架,许多流行的现代框架都使用这种分离的路由方法。为了使其正常工作,您可以在Apache或类似的Web服务器中设置URL重写,在该URL中,通常不再通过直接指向文件的URL路径触发Web应用程序功能。

Zend表现力

https://docs.zendframework.com/zend-expressive/features/router/aura/
https://docs.zendframework.com/zend-expressive/features/router/fast-route/
https://docs.zendframework。 com / zend-expressive / features / router / zf2 /

Zend框架

https://docs.zendframework.com/zend-mvc/routing/

拉拉韦尔

https://laravel.com/docs/5.5/routing

CakePHP

https://book.cakephp.org/3.0/en/development/routing.html


14
真的有这样的变化吗?还是大多数语言/框架从未使用过直接文件系统映射?也许只是PHP赶上了其他理智的步伐?我认为您的观察是错误的,因此没有好的答案。您提到的“转变”也只发生在PHP世界中。但我认为这是一个太宽泛的问题……
rsm,

2
@rsm我也有类似的第一反应,但经过进一步思考,它确实是在许多语言和平台上都已完成的工作,导致它成为漏洞的真正常见来源。
JimmyJames

6
@rsm,这在PHP框架中可能更明显,但是要使用另一种方式-在某个时候,在任何框架真正流行之前,无论是ASP,.NET,PHP,JSP等,网络通常都直接使用-归档方法。为什么所有这些框架都发展为使用分离方法?从技术上讲,直接归档方法仍然可行,并且我确信现代框架可以使用它。还是可以?也许他们做不到,也许有充分的理由说明为什么他们做不到?这些原因是什么?他们甚至没有提供执行此操作的方法或插件,只是完全消除了直接归档。
丹尼斯

2
这不是(部分)关于以URI(和标识符)查看URL(定位符,如何找到资源)吗?
哈根·冯·埃岑

2
古代历史的相关读物:酷的URI不会改变 -Tim Berners-Lee,1998年
Michael Hampton

Answers:


72

网站以其最基本的形式提供静态文件。将URL路径映射到文件路径是最明显的选择。本质上,这是一个只读FTP站点。

然后,人们希望使用一些脚本来更改页面的内容。最简单的方法是将脚本语言嵌入到页面中并通过解释器运行它。同样,给定已经存在的路径->文件路径路由,这很简单。

但是实际上,您现在正在将该文件作为解释器的参数运行。您必须确定请求何时是针对静态文件的,以及何时是针对您需要解释的内容的。

一旦开始使用更高级的编译语言,您将与文件位置更加脱离。

另外,您的Web服务器已经在缓存静态文件并进行各种优化,这意味着击中文件系统是一个例外,而不是规则。在这一点上,旧的链接文件系统路径更多的是障碍而不是帮助。

但是我认为,真正的大变化是当用户想要摆脱路径中的文件扩展名时。获取myPage.asp或myPage.php真是令人困惑,使“正常”人感到困惑,并干扰了SEO。

因为用户看到了路径,所以它已成为Web UI的一部分,因此,它必须完全不受任何技术限制。我们丢失了“ www”,而实际上所有内容都是“ .com”。多个URL将指向同一页面。

如果我通过mydomain.com/sale和www.mydomain.co.uk/products/sale.aspx赚了更多钱,那么我不希望遇到任何技术限制。


7
我一直认为隐藏文件扩展名的部分原因是“默默无闻的安全性”,这使得分辨站点使用哪种技术变得更加困难,从而使其更不容易针对某些服务器的特定附加/已知漏洞利用和当时的技术人员
Caius Jard

20
@CaiusJard是其中的一部分,但另一部分是技术不可知论-在我们的路径中替换了file.html之后,我们不想再被其他开关卡住了(例如,从file.phtml到file.php甚至是file.asp)。但是,由于我们要分离URL和文件系统路径(使用路由或其他方法)以访问从数据库记录和/或其他来源构建的资源,因此为什么在URL中根本没有扩展名?
HorusKol

39
@HorusKol技术不可知论者不只是要更改路径中的所有文件。能够在不破坏客户工作流程和书签且不破坏SEO的情况下更改后端技术的作用非常巨大
Shane

7
有趣的是,从不建议在URI中使用文件扩展名,因此应该早就将其与文件系统分离。我可以找到的最早参考文献是1998年,它实际上早于此答案中描述的大多数发展。
康拉德·鲁道夫

8
过去,mydomain.com / sale仍然有效;它重定向到/ sale /并加载得很好(您的页面是mydomain.com/sale/index.aspx,但是没有人看到index.aspx)。
约书亚

39

您可以参考Roy Fielding的有关代表性状态转移(REST)的白皮书,了解何时以及为什么。我知道的第一个将资源和文件区分开的框架是Ruby on Rails,它将URL的概念引入了代码路由。

REST背后的主要概念是可转换的:

  • URL代表资源
  • 该资源可以具有多种表示形式
  • 如果重组了应用程序,URL应该不会中断
  • 应用程序应包含Web的无状态状态

通过URL直接提供文件的主要缺点是您遇到以下问题:

  • 随着网站的重组,资源链接不断中断
  • 资源和表示形式捆绑在一起

我认为同样重要的是要提供一些平衡:

  • 并非所有资源的重要性都相等。这就是为什么您仍然可以直接提供基于样式的资源(CSS,JavaScript / EcmaScript,图像)的原因
  • HATEOAS这样的REST有一些改进,可以更好地支持单页应用程序。

当您说表示形式时,您的意思是诸如JSON / HTML / TEXT / etc之类的东西吗?我对REST有点熟悉,但是我想即使使用REST,您也必须有某种触发来更改响应表示形式……
Dennis,

@丹尼斯,是的。HTTP具有许多标头,可用于提示所需的形式(developer.mozilla.org/en-US/docs/Web/HTTP/Content_negotiation),而REST就是为了拥抱HTTP的优势。但是,应用程序拥有专有的协商所需内容的方式仍然很常见。
Berin Loritsch

5
CGI(1993),Servlets(1997)和JSP(1999)经常将URL与文件系统解耦,并在REST(2000)之前发布。但是,在确定设计模式流行的原因时,此答案基本上是正确的:REST,Java Struts和Ruby on Rails对21世纪流行的将资源与表示分离的影响很大。
dcorking

1
根据Fielding的论文,“ REST的第一版是在1994年10月至1995年8月之间开发的”
Connor,

1
@dcorking,当时的CGI并没有使URL与文件脱钩,它只是运行文件而不是10中的9次。Servlet可能是最接近的匹配,但是如果您正在谈论路由的概念并设计出一个URL空间, ,它附带了Rails和类似的框架。
Berin Loritsch '18

20

我不认为这是现代 Web应用程序框架的产物,通常它通常是动态页面服务的产物。

过去,大多数情况下都是静态网页,其中软件通过文件路径来处理文件系统中的各个文件。他们之所以这样做,主要是因为URL路径到文件系统路径(将一个目录指定为Web根目录)的1:1映射是显而易见的选择,尽管URL重写(例如,在移动文件后进行重定向)也很常见。

然后是提供动态内容的时代。CGI脚本(以及从中演化而来的所有内容)确实可以在某种类型的数据库的支持下动态创建页面。URL中的GET参数变得司空见惯,例如en.wikipedia.org/w/index.php?title=Path_(computing)

但是,具有仅包含路径段的可读URL 更加用户友好。因此,动态应用程序将简单路径(例如en.wikipedia.org/wiki/Path_(computing))映射到参数,这些映射称为“路由”。

当可用性的重要性得到更广泛的认识时,这种方法随着它的流行而受到欢迎,并且也成为了SEO的一部分。这可能是它直接内置在大型Web框架中的原因。


12

原因之一是,每次请求都从磁盘加载文件很慢,因此Web服务器开始创建在内存中缓存文件的方法,然后,如果您仍然尝试将其保存在内存中,那么它在什么位置上有什么关系呢磁盘?

原因之一是许多Web框架都是用编译语言编写的,因此磁盘上甚至没有文件结构,只有jar文件或其他任何东西。口译语言从编译的语言中借鉴了他们喜欢的想法。

原因之一是希望有更多的语义,动态路线,例如https://softwareengineering.stackexchange.com/questions/363517/how-and-why-did-modern-web-application-frameworks-evolve-to-decouple-url-routes。显然,您不需要/var/www/questions/363517/how-and-why-did-modern-web-application-frameworks-evolve-to-decouple-url-routes.php文件。您曾经在Web服务器配置中制定URL重写规则来创建这样的路由。现在,这只是代码更改,操作更简单。


对于该示例,您不需要重写URL。
Yay295 '18

1
它由识别路径第一部分的代码处理,并使用数字/ 363517 /在数据库中查找问题。与Web服务器本身无关,但与应用程序无关...
Will Crawford

11

其中一个主要原因可能是这种将URI映射到文件路径的方法已导致通过文件路径遍历大量意外释放数据

当您将路径映射到文件系统时,这意味着您需要检查作为请求收到的每个路径是否都映射到客户端应该可以访问的文件。保证不发生这种情况的一种简单方法是消除透明映射并更明确地进行操作。

这不是仅PHP的问题。作为证据,这里是Apache强化指南的相关部分。


1
为什么要下票?
JimmyJames

8

我无法回答这个行业,但是我可以告诉你为什么我在2000年代初期从URL =文件系统转向虚拟“路由”。

使用“老派” PHP,如果您有1000个PHP页面,则将有1000个代表这些页面的PHP文件。每个重复的页眉/页脚都可能包含其他逻辑。现在,假设您需要更改它。您现在手头一团糟!您要么必须更改所有1000个文件,要么最终会在页眉/页脚中混入非常丑陋的代码来处理所有情况。使用虚拟路由,您的页眉/页脚逻辑,数据库连接逻辑和其他初始化都包含一次,即。更好地合作。

另一个原因是避免歧义。随着应用程序的增长,包含的页眉/页脚变得越来越复杂。他们通常嵌套自己的包含内容,这些内容取决于各种事物。在“页面”的PHP文件中,通常会遇到关于变量是否为isset()的歧义。使用虚拟路由,以及在每次页面加载时都加载了所有所需内容的应用程序,您不再需要担心。

最后(尽管还有其他原因,但这是我要列出的最后一个),这1000页中的许多页代表将要重复的代码。因此,在重构为一组适当的类和模板之后,代码会大大简化,并且您无需拥有这1000个文件就可以做所有想做的事情。


您能否再说一遍,为什么最终会得到非常丑陋的代码?我可以看到需要更改1000个文件(假设它是要更新页眉/页脚包含的文件),但是一堆丑陋的代码意味着什么?
丹尼斯,

请参阅我刚刚添加的段落。但是基本上,当您扩展页眉/页脚/初始化代码以处理更多情况时,尤其是如果您有条件地包含其他文件(这是一个坏习惯,但是许多PHP程序员都这样做了),最终您将很难遵循代码。
GrandmasterB

5

对于这种分离为何有益的问题,我不会赘述。主要论点是它将语义(您实际上要访问的内容)与基础实现分开。

认为收益大于成本是给定的-这将是一个单独的问题-不难看出为什么逐步采用它。我认为没有一个事件会导致这种情况,尽管我当然愿意接受这一方面的教育。

至少以我的经验,这最初通常是通过Apache配置完成的,并且大概其他Web服务器也支持此配置。但是,从概念上讲,没有充分的理由说明服务器应该为此承担任务的原因。毕竟,路由是特定于实际应用程序的,因此在此处定义它们是有意义的。

这确实在全球范围内发生了变化,但是正如您所指出的那样,这是逐渐的。这样做的原因几乎可以肯定是非常简单的:好主意会随时间传播。这也是为什么这种变化在全球范围内发生就不足为奇的原因。不是每个人都聚在一起并决定以这种方式这样做。相反,每个项目在认为有好处时都采用了这种方法(不支持它的项目最终消失了)。


1

RFC已经从头开始构建概念,其中包括URI(不将任何语义附加到本地部分)和URL作为特例,引入了类似路径的语义,以允许HTML文档使用相对于文档的链接基本网址。

显而易见的实现是将URL的本地部分直接映射到文件系统,因此这就是简单的设置-无论您使用专用的关系数据库来查找文档,还是利用高度优化的低开销键您已经拥有的超值存储与外部无关,但肯定会影响您提供文件的成本结构。

如果您的Web应用程序具有持久性数据,则成本结构将发生变化:您始终会承担运行该应用程序的开销,并且将URL解码集成到其中使许多功能更易于实现,从而降低了成本。


1

在开始之初,URL会直接映射到服务器上的文件路径,因为它很容易,而且没有其他方法可做,是吗?如果我要求/path/to/index.php,我将从/path/to/index.php网站的根目录开始(通常不是服务器本身),网站应保存在目录或子目录中。

然后,在几年之后,我们开始学习有关重写的知识,该服务所提供的资源与显然要求的资源不同。/request/path/to/index.php可以实际服务/response/path/to/index.php

另一个窍门是躲藏index.php。如果我要求/index.php?foo=bar&baz=qux服务器可以通过隐藏index.php这样的方式进行响应:/?foo=bar&baz=qux,则index.php无论如何实际上一直在服务。

下一步是最重要的一步,是我们学会了将所有 URL 重定向到/index.php。因此,现在,/path/to/some/page被无提示地重定向到/index.php?path/to/some/page。这有点棘手,因为通常每个斜杠代表一个新的子目录,但是在这种情况下,Web服务器被配置为将路径作为参数发送,而不是在其中查找。

现在我们有了这个,我们需要一种完全不同的方式来思考网站的组织方式。以前,它是不同页面的松散集合。现在,所有内容都通过单个输入页面进行路由。这使站点变得更加复杂,但提供了以前无法获得的机会,例如站点范围的用户身份验证,页眉,页脚和样式的统一应用等。

它有效地将您成百上千个应用程序网站(如果您认为每个文件都是其自己的应用程序)变成一个单一,复杂得多但更一致的应用程序。

这是一个巨大的飞跃,因为您不再仅仅通过查看URL就能知道将执行什么代码。现在,您需要对特定框架如何将URL路径转换为代码路径有深刻的理解,并且尽管框架之间存在相似之处,但大多数框架之间的相似之处足以使您需要熟悉一些才能使用代码。

长话短说,这是发现的逐步发展,而不是突然的跳跃,每个开发人员几乎都必须经历相同的发现过程。除非您能真正快速地掌握抽象概念,否则学习曲线相当陡峭。


-1

作为一个长期的Webdev,我认为HTML5左右时代的无导航历史控件history.pushState())的出现使这一点变得实用。在此之前,除非仅更新了片段(/path#fragment),否则您必须重新加载页面以更新URL栏。该片段对于服务器是不可见的(未路由),因此刷新或添加动态页面书签的唯一方法是通过JavaScript。

这对SEO具有重大意义,并导致Google开发了一种很少使用的“ hashbang”架构,该架构要求在服务器端将动态哈希值映射到物理URL。这是笨拙的,并且在机器人之间并不通用,导致了(错误)公理:“蜘蛛无法抓取ajax内容”。但是Ajax内容的好处是显而易见的:例如,尝试使用不带JS的Google地图。

解决方案是一种使用可以在服务器上镜像的值更新URL栏的方法(允许书签和无JS刷新),而无需重新加载页面。一旦具有此功能,开发人员就可以通过简单地更新“主要内容部分”,URL栏和面包屑来“导航”网站。这意味着所有的JS + CSS都不需要重新解析+解析,从而可以更快地进行页面到页面的传输。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.