Laravel 中的流水线与中间件
Web 程序,简明扼要地说,就是个吃网络请求,并吐出响应的东西。 MVC 结构的框架,使用控制器来处理请求,并生成响应。 而 Laravel 框架,还使用流水线(Pipeline)来处理 传入控制器前的请求,和控制器返回的响应。 流水线上的工人,是一个个中间件(Middleware)。 它们就像乐高积木,负责单一的行为。 或负责控制 Cookie,管理 Session; 或负责控制用户的登录验证;又或者控制访问的频率。 通过灵活的中间件注册机制,我们可方便地对这些中间件组装和复用。
运行机制
如果我们马上打开 Pipeline.php
一定会一头雾水,满脑子退堂鼓声。
所以我们得先抛开具体的实现,从一个典型的中间件上,窥一窥流水线的运行机制。
这是一个典型的中间件类,其 handle 方法接收一个请求和一个叫“下一步”的函数, 并返回一个响应。
Laravel 允许我们的中间件返回 mixed,但笔者不推荐这么做。 建议的做法是只允许其返回响应对象,并且是“下一步”所返回的那个, 而不要新建响应对象。遵循这样的规范,可避免诸如 header 丢失这样的意外发生。
这个“下一步”函数,即执行流水线上的下一步,可能是执行下一个中间件, 也可能是执行最终的控制器。总之也是个吃请求,吐响应的东西。 从代码的结构看,流水线的上下游,是嵌套的关系。可以形象地表示为:
一层层的中间件,就像一层层的门卫。 越上游的中间件,就可以越早地接触请求,也能越后手地处理响应,总之权利越大。 这就是流水线的运行机制,层层向内,再层层向外。
中间件的注册
注册中间件有三种方式,其零是全局注册,其一是在路由上注册,其二是在控制器中注册。 不同的情况,应当采取不同的方式。全局注册,显而易见,适合那些统揽全局的中间件。 例如 Laravel 默认的系统维护中间件,当系统处于维护状态时,它将拦截所有请求。 再如强制 HTTPS 的中间件,拦截所有 HTTP 请求,跳转到对应的 HTTPS 请求。 而那些作用于一定范围的中间件,则应当在路由上注册。 例如登录判断中间件,应当注册于一个路由组上,这个路由组包含了所有用户私有的路由。 而不需要登录即可访问的路由,例如登录页面,应放在此组之外。 而那些应用于具体个例的中间件,则要具体分析。属于路由的,要在路由上注册。 耦合于控制器的,则应在控制器上注册。我们可以如此假设: 若该控制器同时注册在多个路由上,此中间件是否都必须要注册。 例如一个上传头像的控制器,上面要注册一个内容读取中间件, 以读取上传的内容,并弥合 PHP 对 POST 和 PUT 请求的差异; 还要注册一个访问频率限制中间件,来避免频繁上传耗费过多服务器带宽。 我们假设它同时注册在用户和管理员的路由组下。 对于内容读取中间件,不论在哪上传,必然都需要,因此要在控制器中注册。 而对于频率限制中间件,则两处可能不同,对管理员的限制可能更宽松。 因此要注册在路由上。总地来说,只有少部分中间件,应注册在全局或控制器上, 而大部分都适合注册在路由上。
控制中心
每个 Laravel 项目中都有一个 \App\Http\Kernel
类,
这便是中间件的控制中心。
全局中间件在这里注册;中间件权利顺序由这里掌管;
在这里,你能为中间件起一个简短的别名,
还能将若干中间件打包成一个组,方便一起注册。
一个初始化的 Laravel 项目中,$routeMiddleware
里,
官方为我们准备了一些常用的中间件。我们大可奉行拿来主义。
而在 $middlewareGroups
中,则预设了 web
和 api
两个组。
它们在 \App\Providers\RouteServiceProvider
中使用,
分别被注册在各自的路由组上。这只是官方提供的一个范式,
根据项目结构不同,我们大可大刀阔斧地改动这个文件,
而不必拘泥于 web 与 api 二分天下的固有结构。
小推荐
末了,推荐一个包 Laroute, 是一个用于 Laravel 的路由工具,同时也是一种路由的语言。可以大幅降低路由文件的语法噪音。 作者即是“王婆”本人了。让我们感受一下 Laroute 带来的清爽: