Laravel 源碼分析---使用 Pipeline 實(shí)現(xiàn)中間件功能

標(biāo)簽: laravel 源碼分析 Pipeline 中間件


在我們了解了 Pipeline 的源碼及工作過程后(見文章Laravel 源碼分析---Pineline),我們來看一下框架中使用 Pipeline 實(shí)現(xiàn)中間件功能的代碼。

框架中間件使用概述。

在 laravel 框架中中間件的配置主要有兩個(gè)地方,一個(gè)是在 App\Http\Kernel 類中進(jìn)行配置,一個(gè)是在路由設(shè)置的時(shí)候進(jìn)行配置。所以 laravel 框架中有兩處管理與使用中間件的地方,一個(gè)是由 Illuminate\Foundation\Http\Kernel 類進(jìn)行管理,在類中 sendRequestThroughRouter 方法使用;另一個(gè)是在 Illuminate\Routing\Router 類中進(jìn)行管理,由 runRouteWithinStack 方法使用。接下來我們分別看這兩處和中間件的管理與使用相關(guān)的代碼。

Kernel 類對中間件的管理與使用

在使用 laravel 框架的過程中,我們會(huì)在 App\Http\Kernel 類中配置我們?nèi)稚У闹虚g件,我們先來看一下框架在此處的默認(rèn)配置代碼

namespace App\Http;

use Illuminate\Foundation\Http\Kernel as HttpKernel;

class Kernel extends HttpKernel
{
    /**
     * The application's global HTTP middleware stack.
     *
     * These middleware are run during every request to your application.
     * 每個(gè)請求都會(huì)經(jīng)過的中間件,全局生效的中間件
     * @var array
     */
    protected $middleware = [
        \Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode::class,
    ];
    
    /**
     * The application's route middleware groups.
     * 應(yīng)用中路由的中間件組
     * @var array
     */
    protected $middlewareGroups = [
        'web' => [
            \App\Http\Middleware\EncryptCookies::class,
            \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
            \Illuminate\Session\Middleware\StartSession::class,
            \Illuminate\View\Middleware\ShareErrorsFromSession::class,
            \App\Http\Middleware\VerifyCsrfToken::class,
            \Illuminate\Routing\Middleware\SubstituteBindings::class,
        ],

        'api' => [
            'throttle:60,1',
            'bindings',
        ],
    ];

    /**
     * The application's route middleware.
     *
     * These middleware may be assigned to groups or used individually.
     * 路由中可能會(huì)用到的中間件的簡化別名
     * @var array
     */
    protected $routeMiddleware = [
        'auth' => \Illuminate\Auth\Middleware\Authenticate::class,
        'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
        'bindings' => \Illuminate\Routing\Middleware\SubstituteBindings::class,
        'can' => \Illuminate\Auth\Middleware\Authorize::class,
        'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
        'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
    ];
}

我們看到這個(gè)類繼承自 Illuminate\Foundation\Http\Kernel 類,主要進(jìn)行中間件的配置。而其父類主要負(fù)責(zé)中間件的管理與使用,我們來看其相應(yīng)源碼。

namespace Illuminate\Foundation\Http;

use Illuminate\Routing\Router;
use Illuminate\Routing\Pipeline;
use Illuminate\Contracts\Foundation\Application;
use Illuminate\Contracts\Http\Kernel as KernelContract;

class Kernel implements KernelContract
{
    /**
     * The application implementation.
     *
     * @var \Illuminate\Contracts\Foundation\Application
     */
    protected $app;

    /**
     * The router instance.
     * 框架路由類的實(shí)例,管理框架的中所有配置的路由,實(shí)現(xiàn)供框架使用的路由功能
     * @var \Illuminate\Routing\Router
     */
    protected $router;

    /**
     * The application's middleware stack.
     * 應(yīng)用全局生效的中間件隊(duì)列
     * @var array
     */
    protected $middleware = [];

    /**
     * The application's route middleware groups.
     * 應(yīng)用的路由中間件組
     * @var array
     */
    protected $middlewareGroups = [];

    /**
     * The application's route middleware.
     * 路由中可能會(huì)用到的中間件的簡化別名
     * @var array
     */
    protected $routeMiddleware = [];

    /**
     * The priority-sorted list of middleware.
     * Forces the listed middleware to always be in the given order. 
     * 中間件的優(yōu)先級,強(qiáng)制下面這些中間件按照給定的順序執(zhí)行
     * @var array
     */
    protected $middlewarePriority = [
        \Illuminate\Session\Middleware\StartSession::class,
        \Illuminate\View\Middleware\ShareErrorsFromSession::class,
        \Illuminate\Auth\Middleware\Authenticate::class,
        \Illuminate\Session\Middleware\AuthenticateSession::class,
        \Illuminate\Routing\Middleware\SubstituteBindings::class,
        \Illuminate\Auth\Middleware\Authorize::class,
    ];

    /**
     * Create a new HTTP kernel instance.
     * 創(chuàng)建一個(gè) HTTP kernel 類的實(shí)例
     * @param  \Illuminate\Contracts\Foundation\Application  $app
     * @param  \Illuminate\Routing\Router  $router
     * @return void
     */
    public function __construct(Application $app, Router $router)
    {
        $this->app = $app;
        $this->router = $router;
        
        //給路由設(shè)置某些中間件的優(yōu)先級
        $router->middlewarePriority = $this->middlewarePriority;

        //設(shè)置路由可能會(huì)用到的中間件組
        foreach ($this->middlewareGroups as $key => $middleware) {
            $router->middlewareGroup($key, $middleware);
        }
        
        //設(shè)置路由可能會(huì)用到的中間件及對應(yīng)的簡化別名
        foreach ($this->routeMiddleware as $key => $middleware) {
            $router->middleware($key, $middleware);
        }
    }

    /**
     * Handle an incoming HTTP request.
     * 執(zhí)行一個(gè) http 請求
     * @param  \Illuminate\Http\Request  $request
     * @return \Illuminate\Http\Response
     */
    public function handle($request)
    {
        try {
            ...
            //將請求通過中間件分發(fā)給路由
            $response = $this->sendRequestThroughRouter($request);
        } catch (Exception $e) {
            $this->reportException($e);

            $response = $this->renderException($request, $e);
        } catch (Throwable $e) {
            $this->reportException($e = new FatalThrowableError($e));

            $response = $this->renderException($request, $e);
        }

        $this->app['events']->fire('kernel.handled', [$request, $response]);

        return $response;
    }

    /**
     * Send the given request through the middleware / router.
     * 發(fā)送 $request,通過全局中間件,并分發(fā)給路由 
     * @param  \Illuminate\Http\Request  $request
     * @return \Illuminate\Http\Response
     */
    protected function sendRequestThroughRouter($request)
    {
        ...
        //根絕全局配置的中間件,設(shè)置中間件任務(wù)隊(duì)列并執(zhí)行
        //$this->app->shouldSkipMiddleware() 是否跳過中間件
        //$this->dispatchToRouter() 將請求分發(fā)給路由
        return (new Pipeline($this->app))
                    ->send($request)
                    ->through($this->app->shouldSkipMiddleware() ? [] : $this->middleware)
                    ->then($this->dispatchToRouter());
    }
    
    /**
     * Get the route dispatcher callback.
     * 返回路由分發(fā)器的回調(diào)函數(shù)
     * @return \Closure
     */
    protected function dispatchToRouter()
    {
        return function ($request) {
            $this->app->instance('request', $request);
            
            //根據(jù)路由的配置分發(fā)請求,
            //并返回相應(yīng)路由配置的控制器和方法執(zhí)行后的響應(yīng)
            return $this->router->dispatch($request);
        };
    }
    
}

我們看到,Kernel 主要是配置全局中間件和路由中間件,管理和執(zhí)行全局中間件。當(dāng)請求通過全局中間件后,將請求分發(fā)到路由。接下來我們來看路由對請求的分發(fā),以及其對路由中間件的管理和執(zhí)行。

Router 類對路由中間件的管理與使用

在 laravel 框架中,路由系統(tǒng)是其非常重要的功能之一,路由中間件的配置和是由路由系統(tǒng)中的 Illuminate\Routing\Router 來管理的,其也是框架路由系統(tǒng)的門面類,向外提供路由系統(tǒng)所需要提供的方法。我們來看一下 Router 類下和路由管理與中間件管理相關(guān)的功能。

namespace Illuminate\Routing;

use Closure;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Illuminate\Support\Collection;
use Symfony\Bridge\PsrHttpMessage\Factory\HttpFoundationFactory;
use Illuminate\Contracts\Routing\Registrar as RegistrarContract;
use Symfony\Component\HttpFoundation\Response as SymfonyResponse;

class Router implements RegistrarContract
{

    /**
     * The IoC container instance.
     * Ioc 容器實(shí)例
     * @var \Illuminate\Container\Container
     */
    protected $container;

    /**
     * The route collection instance.
     * 用戶配置的所有路由的集合
     * @var \Illuminate\Routing\RouteCollection
     */
    protected $routes;

    /**
     * The currently dispatched route instance.
     * 當(dāng)前請求所匹配的路由實(shí)例
     * @var \Illuminate\Routing\Route
     */
    protected $current;

    /**
     * The request currently being dispatched.
     * 傳入的被分發(fā)的請求
     * @var \Illuminate\Http\Request
     */
    protected $currentRequest;

    /**
     * All of the short-hand keys for middlewares.
     * 路由系統(tǒng)中存在簡寫別名的中間件
     * @var array
     */
    protected $middleware = [];

    /**
     * All of the middleware groups.
     * 路由系統(tǒng)所有會(huì)用到的中間件組
     * @var array
     */
    protected $middlewareGroups = [];

    /**
     * The priority-sorted list of middleware.
     * Forces the listed middleware to always be in the given order.
     * 中間件的優(yōu)先級,強(qiáng)制下面這些中間件按照給定的順序執(zhí)行
     * @var array
     */
    public $middlewarePriority = [];

    
    /**
     * 
     * Dispatch the request to the application.
     * 分發(fā)請求到某個(gè)配置的路由
     * @param  \Illuminate\Http\Request  $request
     * @return \Illuminate\Http\Response
     */
    public function dispatch(Request $request)
    {
        $this->currentRequest = $request;

        return $this->dispatchToRoute($request);
    }

    /**
     * Dispatch the request to a route and return the response.
     * 分發(fā)請求到某個(gè)路由,并返回執(zhí)行請求得到的響應(yīng)
     * @param  \Illuminate\Http\Request  $request
     * @return mixed
     */
    public function dispatchToRoute(Request $request)
    {
        //首先,在用戶配置的路由組里面找到匹配請求的路由
        $route = $this->findRoute($request);
        
        //在 $request 設(shè)置路由 resolver 
        //以便路由上的中間件擁有訪問此路由實(shí)例的接口
        $request->setRouteResolver(function () use ($route) {
            return $route;
        });

        ...
        
        //運(yùn)行路由的中間件,讓 $request 通過 $route 配置的中間件
        $response = $this->runRouteWithinStack($route, $request);

        //根據(jù) $request 和 $response,準(zhǔn)備 $response 響應(yīng)對象
        return $this->prepareResponse($request, $response);
    }

    /**
     * 運(yùn)行路由的中間件,讓 $request 通過 $route 配置的中間件
     * @param  \Illuminate\Routing\Route  $route
     * @param  \Illuminate\Http\Request  $request
     * @return mixed
     */
    protected function runRouteWithinStack(Route $route, Request $request)
    {
        //是否應(yīng)該跳過中間件
        $shouldSkipMiddleware = $this->container->bound('middleware.disable') &&
                                $this->container->make('middleware.disable') === true;
    
        $middleware = $shouldSkipMiddleware ? [] : $this->gatherRouteMiddleware($route);
        
        //讓請求 $request 通過 $route 的中間件,
        //并最終返回路由執(zhí)行請求的響應(yīng)
        return (new Pipeline($this->container))
                        ->send($request)
                        ->through($middleware)
                        ->then(function ($request) use ($route) {
                            return $this->prepareResponse(
                                $request, $route->run($request)
                            );
                        });
    }

    /**
     * Gather the middleware for the given route.
     * 獲取解析過的路由的中間件
     * @param  \Illuminate\Routing\Route  $route
     * @return array
     */
    public function gatherRouteMiddleware(Route $route)
    {   
        //獲取路由的中間件并解析
        $middleware = collect($route->gatherMiddleware())->map(function ($name) {
            return (array) $this->resolveMiddlewareClassName($name);
        })->flatten();
        
        //對中間件按照指定優(yōu)先級排序
        return $this->sortMiddleware($middleware);
    }

    /**
     * Resolve the middleware name to a class name(s) preserving passed parameters.
     * 根據(jù) $name 解析出來中間件的信息(保留傳入中間件的參數(shù))
     * $name 可能為匿名函數(shù)(直接返回)
     * $name 為 $this->middleware 中某個(gè)中間件名字
     * $name 為 $this->middlewareGroups 中的某個(gè)中間件組名字
     * $name 為普通字符串
     * @param  string  $name
     * @return string|array
     */
    public function resolveMiddlewareClassName($name)
    {
        $map = $this->middleware;

        if ($name instanceof Closure) {
            return $name;
        } elseif (isset($map[$name]) && $map[$name] instanceof Closure) {
            return $map[$name];
            
        } elseif (isset($this->middlewareGroups[$name])) {
                //解析中間件組
                return $this->parseMiddlewareGroup($name);
        } else {
            // $name 為字符串,解析字符串中的中間件信息 
            list($name, $parameters) = array_pad(explode(':', $name, 2), 2, null);

            return (isset($map[$name]) ? $map[$name] : $name).
                   (! is_null($parameters) ? ':'.$parameters : '');
        }
    }

    /**
     * Parse the middleware group and format it for usage.
     * 根據(jù)中間組信息,解析其中的中間件
     * @param  string  $name
     * @return array
     */
    protected function parseMiddlewareGroup($name)
    {
        $results = [];

        foreach ($this->middlewareGroups[$name] as $middleware) {           
            //遞歸解析中間件組
            if (isset($this->middlewareGroups[$middleware])) {
                $results = array_merge(
                    $results, $this->parseMiddlewareGroup($middleware)
                );

                continue;
            }

            list($middleware, $parameters) = array_pad(
                explode(':', $middleware, 2), 2, null
            );

            if (isset($this->middleware[$middleware])) {
                $middleware = $this->middleware[$middleware];
            }

            $results[] = $middleware.($parameters ? ':'.$parameters : '');
        }

        return $results;
    }

    /**
     * Sort the given middleware by priority.
     * 按照指定優(yōu)先級對中間件進(jìn)行排序
     * @param  \Illuminate\Support\Collection  $middlewares
     * @return array
     */
    protected function sortMiddleware(Collection $middlewares)
    {
        return (new SortedMiddleware($this->middlewarePriority, $middlewares))->all();
    }

    /**
     * Find the route matching a given request.
     * 在用戶配置的路由集合($this->routes)中匹配符合請求 $request 的路由
     * @param  \Illuminate\Http\Request  $request
     * @return \Illuminate\Routing\Route
     */
    protected function findRoute($request)
    {
        $this->current = $route = $this->routes->match($request);

        $this->container->instance('Illuminate\Routing\Route', $route);

        return $route;
    }
    
    /**
     * Register a short-hand name for a middleware.
     * 為中間件 $class 注冊一個(gè)簡寫的名字
     * @param  string  $name
     * @param  string  $class
     * @return $this
     */
    public function middleware($name, $class)
    {
        $this->middleware[$name] = $class;

        return $this;
    }

    /**
     * Register a group of middleware.
     * 注冊一個(gè)中間件組
     * @param  string  $name
     * @param  array  $middleware
     * @return $this
     */
    public function middlewareGroup($name, array $middleware)
    {
        $this->middlewareGroups[$name] = $middleware;

        return $this;
    }
}

我們看到關(guān)于請求 $request 的分發(fā)任務(wù)進(jìn)來后(Kernel 類中 $this->router->dispatch($request) 方法的調(diào)用),Router 類首先在配置的路由集合中找到匹配 $request 的路由,并解析出匹配到的路由的中間件,讓請求 $request 通過中間件,最終返回路由執(zhí)行請求的響應(yīng)。

總結(jié)

Pipeline 和 Pipeline 在中間件中的應(yīng)用是 laravel 框架中很重要的一塊功能,理解這部分代碼的實(shí)現(xiàn),對我們理解框架的設(shè)計(jì)思想具有很重要的作用。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 先說幾句廢話,調(diào)和氣氛。事情的起由來自客戶需求頻繁變更,偉大的師傅決定橫刀立馬的改革使用新的框架(created ...
    wsdadan閱讀 3,193評論 0 12
  • Laravel 學(xué)習(xí)交流 QQ 群:375462817 本文檔前言Laravel 文檔寫的很好,只是新手看起來會(huì)有...
    Leonzai閱讀 8,708評論 2 12
  • 簡介 Laravel 中間件提供了一種方便的機(jī)制來過濾進(jìn)入應(yīng)用的 HTTP 請求, 如ValidatePostSi...
    godruoyi閱讀 936評論 0 6
  • 原文鏈接 必備品 文檔:Documentation API:API Reference 視頻:Laracasts ...
    layjoy閱讀 8,708評論 0 121
  • 過去做事情急,什么東西拿起來就用,不喜歡進(jìn)行系統(tǒng)性的學(xué)習(xí),造成在使用過程中的錯(cuò)誤和低效,現(xiàn)在感覺自己耐心多了,用之...
    馬文Marvin閱讀 2,078評論 0 10

友情鏈接更多精彩內(nèi)容