Laravel 中間件提供了一種方便的機制來過濾進入應用的 HTTP 請求, 如
ValidatePostSize
用來驗證POST
請求體大小、ThrottleRequests
用於限制請求頻率等。php
那Laravel的中間件是怎樣工做的呢?laravel
再說Laravel
中間件前,咱們先來理一理laravel
的啓動流程bootstrap
首先,入口文件index.php
加載了autoload
和引導文件bootstrap
數組
require __DIR__.'/../bootstrap/autoload.php'; $app = require_once __DIR__.'/../bootstrap/app.php';
並在引導文件bootstrap/app.php
中初始化了Application
實例session
$app = new Illuminate\Foundation\Application( realpath(__DIR__.'/../') );
咱們先跳過如何初始化Application(後面會有簡單介紹),再回到入口文件(index.php)中,經過從Application
實例$app
中獲取Http Kernel
對象來執行handle
方法,換取response
。app
$kernel = $app->make(Illuminate\Contracts\Http\Kernel::class); $response = $kernel->handle( $request = Illuminate\Http\Request::capture() ); $response->send(); $kernel->terminate($request, $response);
換取響應後,把響應內容返回給Client
,並執行後續操做(terminate,如關閉session等)。ide
實例化Application
:函數
Laravel的容器並非我此次說的重點,這裏簡單介紹下post
在初始化Application
(啓動容器)時,Laravel
主要作了三件事情ui
註冊基礎綁定
註冊基礎服務提供者
註冊容器核心別名
註冊完成之後,咱們就能直接從容器中獲取須要的對象(如Illuminate\\Contracts\\Http\\Kernel
),即便它是一個Interface
。
獲取Illuminate\Contracts\Http\Kernel類時,咱們獲得的真正實例是 AppHttpKernel
// bootstrap/app.php $app->singleton( Illuminate\Contracts\Http\Kernel::class, App\Http\Kernel::class );
Laravel容器請參考
從容器中得到Http Kernel
對象後,Laravel
經過執行kernel->handle
來換取response
對象。
//Illuminate\Foundation\Http\Kernel.php public function handle($request) { $request->enableHttpMethodParameterOverride(); $response = $this->sendRequestThroughRouter($request); //...... }
enableHttpMethodParameterOverride
方法開啓方法參數覆蓋,便可以在POST
請求中添加_method
參數來僞造HTTP
方法(如post中添加_method=DELETE來構造HTTP DELETE
請求)。
而後Laravel
把請求對象(request
)經過管道流操做。
protected function sendRequestThroughRouter($request) { return (new Pipeline($this->app)) ->send($request) ->through($this->app->shouldSkipMiddleware() ? [] : $this->middleware) ->then($this->dispatchToRouter()); } /** * Get the route dispatcher callback. * * @return \Closure */ protected function dispatchToRouter() { return function ($request) { $this->app->instance('request', $request); return $this->router->dispatch($request); }; }
Pipeline
是laravel的管道操做類。在這個方法中,個人理解是:發送一個$request
對象經過middleware
中間件數組,最後在執行dispatchToRouter
方法。注意,這裏的中間件只是全局中間件。即首先讓Request
經過全局中間件,而後在路由轉發中($this->dispatchToRouter()
),再經過路由中間件
及中間件group
。
因此,到這裏爲止,Laravel
的請求交給了Pipeline
管理,讓咱們來看看這個Pipeline
到底是怎樣處理的。
//Illuminate\Pipeline\Pipeline.php public function then(Closure $destination) { $pipeline = array_reduce( array_reverse($this->pipes), $this->carry(), $this->prepareDestination($destination) ); return $pipeline($this->passable); } protected function prepareDestination(Closure $destination) { return function ($passable) use ($destination) { return $destination($passable); }; } protected function carry() { return function ($stack, $pipe) { return function ($passable) use ($stack, $pipe) { if ($pipe instanceof Closure) { return $pipe($passable, $stack); } elseif (! is_object($pipe)) { list($name, $parameters) = $this->parsePipeString($pipe); $pipe = $this->getContainer()->make($name); $parameters = array_merge([$passable, $stack], $parameters); } else { $parameters = [$passable, $stack]; } return $pipe->{$this->method}(...$parameters); }; }; }
咱們來看看最重要的then
方法, 在這方法中$destination
表示經過該管道最後要執行的Closure
(即上述的dispatchToRouter
方法)。passable
表示被經過管道的對象Request
。
php
內置方法array_reduce
把全部要經過的中間件($this->pipes
)都經過carry
方法($this->pipes
不爲空時)並壓縮爲一個Closure
。最後在執行prepareDestination
。
array_reduce($pipes, callback($stack, $pipe), $destination), 當pipes爲空時,直接執行destination,不然將全部
$pipes
壓縮爲一個Closure
,最後在執行destination
。
列如我有兩個中間件
Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode::class, App\Http\Middleware\AllowOrigin::class,//自定義中間件
將這兩個中間件經過array_reduce
方法時,返回壓縮後的Closure
如:
該Closure
共有三個層, 前面兩個爲兩個中間件,後面個位最後要執行的Closure
(即上述的dispatchToRouter
方法)。
//中間件handle public function handle($request, Closure $next) { }
在第一個經過的中間件(此處是CheckForMaintenanceMode
)handle
方法中,dump($next)
以下
在第二個經過的中間件(共兩個,此處是AllowOrigin
)handle
方法中,dump($next)
以下
由此可知,中間件在執行$next($request)
時,表示該中間件已正常經過,並期待繼續執行下一個中間件。直到全部中間件都執行完畢,最後在執行最後的destination
(即上述的dispatchToRouter
方法)
若是上述
array_reduce
有地方難懂的,能夠參考這邊文章PHP 內置函數 array_reduce 在 Laravel 中的使用
以上是Laravel
在經過全局中間件時的大體流程,經過中間件group和路由中間件
也是同樣的, 都是採用管道流操做,詳情可翻閱源碼
Illuminate\Routing\Router->runRouteWithinStack