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 帶來的清爽: