laravel middleware 源码解析
middleware 的作用是啥?
我这保存着一份初学 php 原生 MCA 框架的代码让大家怀怀旧:

这个框架在访问网站 ⌈http://localhost/m=Home&c=Index&a=index⌋ 时直接调用控制器方法,也就是直达目标。
然而,我们需要在到达控制器前或者调用控制器后执行一些需求。
middleware 在 Laravel 中应用的,比如权限中间件 ⌈处理请求前⌋:
<?php
namespace App\Http\Middleware;
// 权限中间件
class AdminAuth {
public function handle($request, Closure $next) {
if (!auth()->check()) {
return redirect(route('login'));
}
return $next($request);
}
}
跨域设置 [得到响应后]:
<?php
namespace App\Http\Middleware;
// 跨域设置
class Cors {
public function handle($request, Closure $next){
$response = $next($request);
$response->withHeaders([
'Access-Control-Allow-Origin' => '*',
..
]);
return $response;
}
}
为了能有一个大概的印象,上一图

中间件之间传递的是同一个 $request 对象
进入正题,一个重要的概念是,一个请求在未达到控制前,以及到达控制器时,层层中间件之间一直传递的都是同一个 $request 对象。哪怕在控制器中见到的自动注入的 $request 或者 request() 获得的实例。
下面先说一说控制器中 Request $request 为何是中间件过来的 $request,这涉及服务容器的知识。
use Illuminate\Http\Request;
class IndexController extends Controller{
public function index(Request $request) {
return 200;
}
}
简单来说,在应用初始化时,已将 \Illuminate\Http\Request::class alias 'request'
'request' => [\Illuminate\Http\Request::class, \Symfony\Component\HttpFoundation\Request::class],
也就是说容器在实例化 ⌈此处应该叫解析更恰当⌋ \Illuminate\Http\Request 时会先查找关联的键值 'request' 绑定在容器中的实例

那什么时候绑定的?下文中会介绍。由此,request() 得到的实例也就容易理解了

$request 对象
这得回到 index.php

Illuminate\Http\Request 类是 $request 实例的来源,capture() 是捕获,简单说就是从 $_GET $_POST $_SERVER $_COOKIE.. 捕获 http 请求封装为对象。所以这一部分我们知道了

经过中间件
接下来是 http 内核 Kernel 处理 $request 了。
$kernel = $app->make(Illuminate\Contracts\Http\Kernel::class);
我们直接看 App\Http\Kernel::class 即可,其中定义了大量的中间件,可见中间件是 Http 核心的重要内容。

代码追踪 handle() 方法


到达核心代码

前文中提到的容器中 $request 实例是什么时候绑定到键值 'request' 的便在此处。
继续,Pipeline 翻译 ⌈管道⌋,从语义上,send($request) 是发送 $request 实例,through 是经过中间件,then 是然后分发路由,得到响应最后 return。
然而 through 并非是执行了经过中间件的动作,源码中仅仅是向 Pipeline 中 ⌈注册⌋ 中间件

所以真正经过中间件的动作还是 then 方法。
Pipeline
接下来是代码较难懂的一部分了,也就是 then 方法。

理解中间件的原理最重要的是 array_reduce() 函数,可以参考下之前的文章 博客:middleware 原理
博客:laravel middleware 之 array_reduce 详解
整个中间件就是闭包套娃,像个洋葱,最里面的闭包是 ⌈最终目标⌋ 也就是控制器方法,最后传入参数 $request 所有的闭包一层层的抛开。
carry 翻译 ⌈运送⌋,middleware 的 handle 方法就是 carry() 方法中调用的:


每个中间件都将被封装成一个闭包,都有三个阶段,前置操作、传递请求、后置操作然后返回响应。控制器方法为最后目标,亦被封装为一个闭包。

本作品采用《CC 协议》,转载必须注明作者和本文链接
关于 LearnKu
推荐文章: