Slim研读笔记六之应用主体(中)

本文是Slim框架研读笔记的一部分,主要探讨了中间件的概念及其工作原理。中间件允许在请求和响应处理前后添加自定义代码,常用于安全验证等场景。在Slim中,中间件以同心圆结构组织,请求和响应对象从外到内,再从内到外通过中间件层,最后形成HTTP响应返回给客户端。

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

你可以在你的 Slim 应用之前(before) 和 之后(after) 运行代码来处理你认为合适的请求和响应对象。这就叫做中间件。为什么要这么做呢?比如你想保护你的应用不遭受跨站请求伪造。也许你想在应用程序运行前验证请求。中间件对这些场景的处理简直完美。


什么是中间件?

从技术上来讲,中间件是一个接收三个参数的可回调(callable)对象:

  1. \Psr\Http\Message\ServerRequestInterface - PSR7 请求对象
  1. \Psr\Http\Message\ResponseInterface - PSR7 响应对象
  1. callable - 下一层中间件的回调对象

它可以做任何与这些对象相应的事情。唯一硬性要求就是中间件必须返回一个 \Psr\Http\Message\ResponseInterface 的实例。 每个中间件都 应当调用下一层中间件,并讲请求和响应对象作为参数传递给它(下一层中间件)。


中间件是如何工作的?

不同的框架使用中间件的方式不同。在 Slim 框架中,中间件层以同心圆的方式包裹着核心应用。由于新增的中间件层总会包裹所有已经存在的中间件层。当添加更多的中间件层时同心圆结构会不断的向外扩展。

当 Slim 应用运行时,请求对象和响应对象从外到内穿过中间件结构。它们首先进入最外层的中间件,然后然后进入下一层,(以此类推)。直到最后它们到达了 Slim 应用程序自身。在 Slim 应用分派了对应的路由后,作为结果的响应对象离开 Slim 应用,然后从内到外穿过中间件结构。最终,最后出来的响应对象离开了最外层的中间件,被序列化为一个原始的 HTTP 响应消息,并返回给 HTTP 客户端。下图清楚的说明了中间件的工作流程


一起看下Slim是如何处理中间件的,在应用主体类App中

class App
{
    use MiddlewareAwareTrait;
......

继续研读该特制的实现过程

/**
 * Middleware
 * 这是一个启用同心中间件的内部类。
 * This is an internal class that enables concentric middleware layers. This
 * class is an implementation detail and is used only inside of the Slim
 * application; it is not visible to—and should not be used by—end users.
 */
trait MiddlewareAwareTrait
{
    /**
     * 中间件调用堆栈的栈顶
     * Tip of the middleware call stack
     *
     * @var callable
     */
    protected $tip;

    /**
     * 中间件栈锁
     * Middleware stack lock
     *
     * @var bool
     */
    protected $middlewareLock = false;

    /**
     * 增加中间件
     * Add middleware
     * 这个方法增加一个新的中间件到应用中间件栈
     * This method prepends new middleware to the application middleware stack.
     * 中间件以回调形式存在:包括:1.一个请求对象 2.一个响应对象 3.next中间件回调函数
     * @param callable $callable Any callable that accepts three arguments:
     *                           1. A Request object
     *                           2. A Response object
     *                           3. A "next" middleware callable
     * @return static
     *
     * @throws RuntimeException         If middleware is added while the stack is dequeuing
     * @throws UnexpectedValueException If the middleware doesn't return a Psr\Http\Message\ResponseInterface
     */
    protected function addMiddleware(callable $callable)
    {
        // 若中间件锁住,抛出异常
        if ($this->middlewareLock) {
            throw new RuntimeException('Middleware can’t be added once the stack is dequeuing');
        }

        // 栈顶元素为空,则初始化栈(实质先存储一个元素),该步骤之后
        // $this->tip保存当前应用主体类$app实例
        if (is_null($this->tip)) {
            $this->seedMiddlewareStack();
        }
        // 这一步是关键,不断从栈中取出元素(中间件)
        // call_user_func的$next参数为上一中间件(闭包)的返回值
        $next = $this->tip;
        $this->tip = function (
            ServerRequestInterface $request,
            ResponseInterface $response
        ) use (
            $callable,
            $next
        ) {
            $result = call_user_func($callable, $request, $response, $next);
            if ($result instanceof ResponseInterface === false) {
                throw new UnexpectedValueException(
                    'Middleware must return instance of \Psr\Http\Message\ResponseInterface'
                );
            }

            return $result;
        };

        return $this;
    }

    /**
     * 使用一个回调填充中间件栈
     * Seed middleware stack with first callable
     *
     * @param callable $kernel The last item to run as middleware
     *
     * @throws RuntimeException if the stack is seeded more than once
     */
    protected function seedMiddlewareStack(callable $kernel = null)
    {   // 填充前提是栈顶元素为空
        if (!is_null($this->tip)) {
            throw new RuntimeException('MiddlewareStack can only be seeded once.');
        }

        // 将当前类(App)实例赋值给$kernel
        if ($kernel === null) {
            $kernel = $this;
        }

        // $this->tip元素即为当前应用主体app实例
        $this->tip = $kernel;
    }

    /**
     * 调用中间件
     * Call middleware stack
     *
     * @param  ServerRequestInterface $request A request object
     * @param  ResponseInterface      $response A response object
     *
     * @return ResponseInterface
     */
    public function callMiddlewareStack(ServerRequestInterface $request, ResponseInterface $response)
    {
        if (is_null($this->tip)) {
            $this->seedMiddlewareStack();
        }
        /** @var callable $start */
        // 取出栈顶元素(闭包),取的过程要锁栈,取出之后再打开
        // 锁栈的过程是不可增加中间件的
        $start = $this->tip;
        $this->middlewareLock = true;
        $response = $start($request, $response);
        $this->middlewareLock = false;
        return $response;
    }
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值