动手实现Hyperf中间件

喜欢而已 提交于 2020-03-05 07:03:39

Hyperf的中间件跟Java中的FilterChain类似,其作用是在调用controller之前对请求对象进行拦截,进行某些操作,再传递给下一个中间件或者直接返回输出响应。如Hyperf手册中所示,中间件是一个洋葱模型,其实现原理就是一个递归调用,代码思路如下:

<?php

interface MiddlewareInterface{
    public function process(Response $response,RequestHandler $requestHandler);
}

class Middleware1 implements MiddlewareInterface{
    public function process(Response $response,RequestHandler $requestHandler){
        echo "Middleware1 before.<br/>";
        $requestHandler->handle($response);
        echo "Middleware1 after.<br/>";
        return $response;
    }
}

class Middleware2 implements MiddlewareInterface{
    public function process(Response $response,RequestHandler $requestHandler){
        echo "Middleware2 before.<br/>";
        // 根据具体业务做一些校验
        $valid = true;
        if ($valid) {
            $requestHandler->handle($response);
        } else {
            return $response->setResponse("stop at Middleware2");
        }

        echo "Middleware2 after.<br/>";
    }
}

class Middleware3 implements MiddlewareInterface{
    public function process(Response $response,RequestHandler $requestHandler){
        echo "Middleware3 before.<br/>";
        $requestHandler->handle($response);
        echo "Middleware3 after.<br/>";
        return $response;
    }
}

class IndexController{
    public function index(Response $response){
        echo "controller.<br/>";
        return $response->setResponse('this is index page');
    }
}

class Response{
    private $response;
    public function getResponse(){
        return $this->response;
    }

    public function setResponse($response){
        $this->response = $response;
    }
}

class RequestHandler{
    private $controller;
    private $action;
    private $middlewareChain;
    private $middlewareOffset = 0;

    public function __construct($route)
    {
        $this->controller = new $route[0];
        $this->action = $route[1];
        $this->middlewareChain = $route[2];
    }

    public function handle($response){
        if ($this->middlewareOffset == count($this->middlewareChain)){
            return $this->controller($response);
        } else {
            $middleware = new $this->middlewareChain[$this->middlewareOffset++]();
            return $middleware->process($response,$this);

        }
    }

    public function controller($response){
        return call_user_func([$this->controller,$this->action],$response);
    }
}

// 匹配到的路由信息
$route = ['IndexController','index',[Middleware1::class,Middleware2::class,Middleware3::class]];
// 实例化,这里直接new,但在框架内所有对象的new操作都由容器进行管理注入
$requestHandler = new RequestHandler($route);
// 处理请求流程 & 响应
echo $requestHandler->handle(new Response())->getResponse();

根据请求匹配到具体路由信息,实例化一个Response对象传入RequestHandler的handle方法中,handle沿着中间件链进行递归调用,最终执行controller的方法返回Response对象,输出响应。上述示例响应如下:

可以在某个中间件截断请求处理流程,直接返回Response对象,例如将上述代码的Middleware2中的$valid改为false,响应如下:

中间件的应用场景有:

  • 对请求头进行设置,如允许跨域
  • 对公共的请求参数进行校验,如token校验、黑白名单校验
  • 对请求对象进行预处理,如将JWT的payload解析获取用户id并存储在对象属性中,方便之后进行获取查询
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!