【问题标题】:How handle() method of Laravel middleware is called using 'Clousre $next' in another Middleware?如何在另一个中间件中使用“Clousre $next”调用 Laravel 中间件的 handle() 方法?
【发布时间】:2017-07-01 03:20:16
【问题描述】:

这是来自 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);
}

现在,使用 $next($request) 为另一个中间件调用此方法。我的理解是 handle() 方法被翻译成 $next。我想知道这在幕后是如何发生的。

【问题讨论】:

  • 查找代码?
  • 它是堆栈中的下一个中间件闭包。
  • @MagnusEriksson 看看问题的描述。我知道它的关闭,因为它已经在方法中暗示了。我想知道的是handle()方法是如何翻译成闭包$next的。
  • @MayankKumar 我的回答可能会让你知道 Closure 的工作原理。
  • 再一次,实际上这里发生的事情是laravel中有很多中间件,它们都有handle(request $request,closure $next)方法。现在,每个中的 $next 都映射到其他的 handle() 。即当 $next 运行时,它运行另一个中间件的 handle() 方法。

标签: php laravel laravel-middleware


【解决方案1】:

要将请求更深入地传递到应用程序(允许中间件“传递”),只需使用 $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());

PipelineIlluminate\Routing\Pipeline,它扩展了 Illuminate\Pipeline\Pipeline

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

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

    return $this->router->dispatch($request);
})

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

然后,在then() 方法中,会发生上面提到的array_reducearray_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);
}

假设我们有 3 个中间件:

[
    One::class,
    Two::class,
    Three::class,
];

上面的$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);

            });
        };);
    };);
};

希望这会有所帮助!

【讨论】:

  • 您能详细说明一下吗?我认为所有的魔法都发生在 Illuminate/Pipeline/Pipeline.php 中。一个简单的例子会很棒。
  • @MayankKumar 我已经添加了更多内容。
  • 如果我想在不使用管道的情况下实现这个,我应该如何处理?
  • @RossWilson 您的解释在 laravel 的上下文中很棒。我发现此功能非常强大,并想对其进行概括。我怎样才能以最简单的形式实现它(没有管道和 laravel)?
  • @MayankKumar 这确实超出了这个问题的范围,它完全取决于您如何处理对应用程序的请求和响应,如何将中间件分配给不同的路由/请求等。说,在非常基本的层面上,您只需创建一个要调用的类/函数数组,确保每个类/函数都收到一个闭包,然后基本上使用上面的简化示例
【解决方案2】:

接下来是一个闭包,一个匿名函数的变量。在您的代码中,您有return $next($request);。 $next 是一个闭包,基于您的第二个方法参数。这意味着您的方法的返回值是匿名函数返回的值。

例如:

// this is the Closure.
$next = function ($parameter) {
    return $parameter . ' This message is modified by $next';
};

public function handle($message, Closure $next)
{
    return $next($message);
}

// test the output
$message = 'Hello World!';
echo handle($message, $next); // output will be 'Hello World! This message is modified by $next'

【讨论】:

    猜你喜欢
    • 2017-07-14
    • 2016-03-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-12-07
    相关资源
    最近更新 更多