如何在另一个中间件中使用“Clousre $next”调用 Laravel 中间件的 handle() 方法?

May*_*mar 3 php laravel laravel-middleware

这是 Laravel 的ValidatePostSize 中的 handle() 方法:

public function handle($request, Closure $next)
{
    $max = $this->getPostMaxSize();

    if ($max > 0 && $request->server('CONTENT_LENGTH') > $max) {
        throw new PostTooLargeException;
    }

    return $next($request);
}
Run Code Online (Sandbox Code Playgroud)

现在,使用$next($request)为另一个中间件调用此方法。我的理解是handle()方法被转换为$next。我想知道这是如何发生在引擎盖下的。

Rwd*_*Rwd 8

要将请求更深入地传递到应用程序中(允许中间件“传递”),只需使用 $request 调用 $next 回调。 https://laravel.com/docs/5.4/middleware#defining-middleware

当 Laravel 处理请求时,它会运行堆栈中所有适用的中间件。中间件可以设置为在路由/控制器方法之前和/或之后运行。

为了能够做到这一点,Laravel 使用Illuminate\Pipeline\Pipeline. 本质上,它用于array_reduce遍历中间件堆栈,然后返回一个Closure以执行该中间件。这样做的好处在于array_reverse允许将下一个中间件执行传递给前一个执行。


再详细说明一点:

Illuminate\Foundation\Http\Kernel@handle被调用时,它会建立响应,sendRequestThroughRouter其中包含以下内容:

return (new Pipeline($this->app))
            ->send($request)
            ->through($this->app->shouldSkipMiddleware() ? [] : $this->middleware)
            ->then($this->dispatchToRouter());
Run Code Online (Sandbox Code Playgroud)

PipelineIlluminate\Routing\Pipeline哪个延伸Illuminate\Pipeline\Pipeline

then()上面的方法本质上是:

->then(function ($request) {
    $this->app->instance('request', $request);

    return $this->router->dispatch($request);
})
Run Code Online (Sandbox Code Playgroud)

那么意味着我们从一个接受最终结果的闭包开始(记住此时不会调用闭包)。

然后,在then()方法中,发生了上面提到的array_reduceandarray_reverse部分。


这是then()方法中实际发生时间的简化示例(假设您知道如何array_reduce工作):

function then(Closure $destination)
{
    $pipeline = array_reduce(

        array_reverse($this->middlewares),

        //Remember $nextClosure is going to be the closure returned 
        //from the previous iteration

        function ($nextClosure, $middlewareClass) {

            //This is the $next closure you see in your middleware
            return function ($request) use ($nextClosure, $middlewareClass) {

                //Resolve the middleware
                $middleware = app($middlewareClass);

                //Call the middleware
                return $middleware->{$this->method}($request, $nextClosure);
            };
        },

        //The finial closure that will be called that resolves the destination

        function ($request) use ($destination) {
            return $destination($request);
        }
    );

    return $pipeline($this->request);
}
Run Code Online (Sandbox Code Playgroud)

假设我们有 3 个中间件:

[
    One::class,
    Two::class,
    Three::class,
];
Run Code Online (Sandbox Code Playgroud)

$pipeline上面的变量基本上是:

function ($request) {

    return app(One::class)->handle($request, function ($request) {

        return app(Two::class)->handle($request, function ($request) {

            return app(Three::class)->handle($request, function ($request) {

                return $destination($request);

            });
        };);
    };);
};
Run Code Online (Sandbox Code Playgroud)

希望这可以帮助!