laravel Pipeline管道 与middleware中间件

本文深入探讨laravel中的Pipeline管道原理,通过send(), through()和then()等核心方法解析其工作方式。同时,介绍了Middleware中间件的概念,包括全局中间件如CheckForMaintenanceMode的工作流程,并对比了thinkphp、fend和easyswoole中的middleware实现。" 125052194,10438664,HTML扫雷游戏实现,"['HTML', 'CSS', '前端框架']

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在laravel中,middleware是用管道实现的,要理解middleware,首先要了解Pipeline。
下文会对代码做一定简化方便理解

Pipeline 管道

以全局中间件为例,首先在kernel中定义middleware数组

    protected $middleware = [
        \App\Http\Middleware\TrustProxies::class,
        \Fruitcake\Cors\HandleCors::class,
        \App\Http\Middleware\CheckForMaintenanceMode::class,
        \Illuminate\Foundation\Http\Middleware\ValidatePostSize::class,
    ];

核心代码

    protected function sendRequestThroughRouter($request)
    {
    	// something...
    	
        return (new Pipeline($this->app))	//	实例化Pipeline	
                    ->send($request)				//带上请求实例
                    ->through($this->middleware)		//穿过中间件
                    ->then($this->dispatchToRouter()); 	//将请求派发到路由地址中
    }
send()

通过管道处理的对象实例 这次为$request

        public function send($passable) {
       	 	$this->passable = $passable;
        	return $this;
    }
through()

要通过的管道 middleware

        public function through($pipes) {
        	$this->pipes = is_array($pipes) ? $pipes : func_get_args();
        	return $this;
    	}
then()

请求到底是如何通过middleware,就是这个方法实现

    public function then(Closure $destination) {
        $pipeline = array_reduce(
            array_reverse($this->pipes), $this->carry(), $this->prepareDestination($destination)
        );
        return $pipeline($this->passable);
    }

方法中,最关键的就是函数array_reduce,此函数参数如下
array_reduce ( array $array , callable $callback [, mixed $initial = NULL ] ) : mixed
函数将对数组 array 每一个元素使用 回调函数callback 进行迭代,initial为迭代时的初始值
在此场景中,就是carry()函数迭代所有middleware数组中的元素,以闭包函数为初始值
再观察carry()函数

    protected function carry() {
        return function ($stack, $pipe) {
            return function ($passable) use ($stack, $pipe) {
            	//实例化
				$pipe = $this->getContainer()->make($pipe);
				//$this->method 就是handle
                $carry = $pipe->{$this->method}(...$parameters);
                $parameters = array_merge([$passable, $stack], $parameters);
                return $carry;
            };
        };
    }

将上次middleware中handle执行的闭包,传入下个middleware中,直至结束
如果只注册上面四个个全局middleware的话,then()方法大概执行逻辑如下
TrustProxies ValidatePostSize ValidatePostSize HandleCors

    public function then1(Closure $destination){
	        return function ()use ($destination){
	            return (new ValidatePostSize)->handle(...function ()use ($destination){
	                return (new CheckForMaintenanceMode)->handle(...function ()use ($destination){
	                    return (new HandleCors)->handle(...function ()use ($destination){
	                        return (new TrustProxies )->handle(...function ()use ($destination){
                           		 return $this->prepareDestination($destination);
	                        });
	                    });
	                });
	            });
	        };
        }

所以也解释了为什么要用array_reverse 倒排数组,先进后出,倒排一下保证是定义数组的时候的顺序

Middlerware 中间件

中间件,顾名思义,就是放在中间的物品。
偷个很形象的图
原贴https://blog.youkuaiyun.com/weixin_34194702/article/details/88919329?utm_medium=distribute.pc_relevant_t0.none-task-blog-OPENSEARCH-1.channel_param&depth_1-utm_source=distribute.pc_relevant_t0.none-task-blog-OPENSEARCH-1.channel_param
在app前后可分前置后置,在内层外层可分全局部分,甚至可以匹配路由。
在使用时,可以使用中间件做一些通用的处理
以全局前置中间件CheckForMaintenanceMode为例

CheckForMaintenanceMode

字面意思 检查是否为维护模式。由上文,我们只需关系核心方法handle

    public function handle($request, Closure $next)
    {
    	//down文件存在即维护模式
        if ($this->app->isDownForMaintenance()) {
            $data = json_decode(file_get_contents($this->app->storagePath().'/framework/down'), true);
			//是否为配置中允许访问的ip
            if (isset($data['allowed']) && IpUtils::checkIp($request->ip(), (array) $data['allowed'])) {
                return $next($request);
            }
			//是否为配置中允许访问的路由
            if ($this->inExceptArray($request)) {
                return $next($request);
            }
			//真的维护就抛出异常
            throw new MaintenanceModeException($data['time'], $data['retry'], $data['message']);
        }
		//到下一层中间件中验证
        return $next($request);
    }

laravel也支持自定义middleware,感兴趣的可以去看文档

thinkphp中的middleware

    public function pipeline(string $type = 'global')
    {
        return (new Pipeline())
        	//	关键函数换成了array_map 不过也是迭代数组+匿名函数
            ->through(array_map(function ($middleware) {
                return function ($request, $next) use ($middleware) {
                    [$call, $params] = $middleware;
                    if (is_array($call) && is_string($call[0])) {
                    	//$call[0] 中间件类名  $call[1] handle
                    	//
                        $call = [$this->app->make($call[0]), $call[1]];
                    }
                    $response = call_user_func($call, $request, $next, ...$params);

                    if (!$response instanceof Response) {
                        throw new LogicException('The middleware must return Response instance');
                    }
                    return $response;
                };
            }, $this->sortMiddleware($this->queue[$type] ?? [])))
            ->whenException([$this, 'handleException']);
    }

fend中的middleware

    public function handle($request)
    {
    	//直接用数组下标操作
        if (! isset($this->middlewares[$this->offset]) && ! empty($this->coreHandler)) {
            $handler = $this->coreHandler;
        } else {
            $handler = $this->middlewares[$this->offset];
            is_string($handler) && $handler = new $handler();
        }
        //handle 方法改成 process
        if (! method_exists($handler, 'process')) {
            throw new FendException(sprintf('Invalid middleware, it have to provide a process() method.'));
        }
        return $handler->process($request, $this->next());
    }
	
    protected function next()
    {
        $this->offset ++;
        return $this;
    }

easyswoole

easywoole并没有实现middleware,使用onRequest()和 afterAction()进行控制器的前后操作

        try {
        	//为啥不顺便叫beforeAction呢
            if ($this->onRequest($actionName) !== false) {
                    $forwardPath = $this->$actionName();
            }
        } catch (\Throwable $throwable) {
            $this->onException($throwable);
        } finally {
            try {
                $this->afterAction($actionName);
            } catch (\Throwable $throwable) {
                $this->onException($throwable);
            } 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值