laravel中的中间件作为一个请求与响应的过滤器,主要分为两个功能。
1、在请求到达控制器层之前进行拦截与过滤,只有通过验证的请求才能到达controller层
2、或者是在controller中运算完的数据或页面响应返回前进行过滤,通过验证的响应才能返回给客户端
中间件一般通过artisan命令创建
php artisan make:middleware 中间件名称
命令行创建的中间件会保存在/app/Http/Middleware文件夹里
这些中间件需要手动添加在/app/Http/Kernel.php文件的配置中,这个文件继承了kernel核心,在应用入口初始化kernel核心时,vendor\laravel\framework\src\Illuminate\Foundation\Http\Kernel.php这个kernel文件中的配置便覆盖了kernel核心中的相关配置,其中当然便包括了middleware中间件。我们可以在kernel类的构造方法第一行添加打印语句,分别打印$this->middleware,$this->middlewareGroups然后我们就会看见之前写在/app/Http/Kernel.php文件中的中间件了。
不过不止这里一处,在路由加载的时候,也会加载一次系统默认的web数组中的中间件,在返回响应对象的时候进行过滤,这个稍后再看。
接下来看一下中间件是在哪里开始过滤的,在第一篇关于入口文件的博文中,我们了解到laravel是从kernel核心中的handle方法开始的。
public function handle($request) { try { //启用http方法覆盖参数 $request->enableHttpMethodParameterOverride(); //通过路由发送请求 $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']->dispatch( new Events\RequestHandled($request, $response) ); return $response; } protected function sendRequestThroughRouter($request) { //将请求存入容器 $this->app->instance('request', $request); //清除facade门面 Facade::clearResolvedInstance('request'); //初始化引导 $this->bootstrap(); //让请求进入中间件 return (new Pipeline($this->app)) ->send($request) ->through($this->app->shouldSkipMiddleware() ? [] : $this->middleware) ->then($this->dispatchToRouter()); }
kernel核心的sendRequestThroughRouter方法中最后的return 语句,便开始使用中间件来对系统接到的请求开始进行过滤了。
上一篇博文中,我们提到了装饰模式与laravel在此基础上变种来的管道模式,便是在这里开始运用。
从Pipeline对象一路跳转,发现这个对象的基类为laravel\vendor\laravel\framework\src\Illuminate\Pipeline\Pipeline.php。管道对象的构造方法把laravel系统容器传入了其中。这里不再赘述管道模式的原理,直接来看关键方法then。
1 public function then(Closure $destination) 2 { 3 //array_reduce — 用回调函数迭代地将数组简化为单一的值 4 $pipeline = array_reduce( 5 //array_reverse将数组反向排序,$this->carry()返回一个闭包函数用来迭代 6 array_reverse($this->pipes), $this->carry(), $this->prepareDestination($destination) 7 ); 8 9 return $pipeline($this->passable); 10 } 11 12 protected function carry() 13 { 14 return function ($stack, $pipe) { 15 return function ($passable) use ($stack, $pipe) { 16 if (is_callable($pipe)) { 17 // If the pipe is an instance of a Closure, we will just call it directly but 18 // otherwise we'll resolve the pipes out of the container and call it with 19 // the appropriate method and arguments, returning the results back out. 20 return $pipe($passable, $stack); 21 } elseif (! is_object($pipe)) { 22 //解析字符串 23 list($name, $parameters) = $this->parsePipeString($pipe); 24 25 // If the pipe is a string we will parse the string and resolve the class out 26 // of the dependency injection container. We can then build a callable and 27 // execute the pipe function giving in the parameters that are required. 28 //从容器中取出相应的对象 29 $pipe = $this->getContainer()->make($name); 30 $parameters = array_merge([$passable, $stack], $paramet