分享一个实现控制器中间件方法的方式

小Z先生
  • 首先是实现一个中间件ControllerMiddleware
<?php

namespace common\middleware;

use ReflectionClass;
use Throwable;
use Webman\Http\Request;
use Webman\Http\Response;
use Webman\MiddlewareInterface;

/**
 * 控制器中间件
 * Class ControllerMiddleware
 * @package common\middleware
 */
class ControllerMiddleware implements MiddlewareInterface
{
    public function process(Request $request, callable $handler): Response
    {
        $controller = new ReflectionClass($request->controller);
        $exists = method_exists($request->controller, "middlewares");
        //验证控制器或者其父类是否有middlewares方法
        if (!$exists) {
            return $handler($request);
        }
        $refMethod = $controller->getMethod('middlewares');
        $middlewares = $refMethod->invokeArgs(null, []); //执行静态方法获取到控制器中间件配置
        if ($middlewares) {
            $actionMiddlewares = $middlewares[$request->action] ?? []; //标识只有这个方法需要执行(只有方法执行)
            $controllerMiddlewares = $middlewares[""] ?? []; //标识整个控制器都需要执行(控制器全局)
            $allMiddlewares = array_merge($actionMiddlewares, $controllerMiddlewares);
            if ($allMiddlewares) {
                $callList = [];
                foreach ($allMiddlewares as $m) {
                    if (!is_a($m,MiddlewareInterface::class,true)){
                        continue;
                    }
                    //因为有的是直接配置 Test2Middleware::class
                    //有的则可能是  new Test2Middleware('xxx') 传参的
                    if (is_string($m)) {
                        $callList[] = [new $m, 'process'];
                    } else {
                        $callList[] = [$m, 'process'];
                    }
                }
                $callback = array_reduce($callList, function ($carry, $pipe) {
                    return function ($request) use ($carry, $pipe) {
                        try {
                            return $pipe($request, $carry);
                        } catch (Throwable $e) {
                            throw $e;
                        }
                    };
                }, function ($request) use ($handler) {
                    return $handler($request);
                });
                return $callback($request, $handler);
            }
        }
        return $handler($request);
    }
}
  • 然后配置到config/middleware.php中 可以配置到超全局@或者全局,模块的都行
'@' => [
        ControllerMiddleware::class,
],
  • 新增一个测试用的中间件
<?php

namespace common\middleware;

use Webman\Http\Request;
use Webman\Http\Response;
use Webman\MiddlewareInterface;

/**
 * 测试中间件
 * Class CheckDuplicateMiddleware
 * @package common\middleware
 */
class TestMiddleware implements MiddlewareInterface
{
    public function __construct(protected $key = "")
    {
    }

    public function process(Request $request, callable $handler): Response
    {
        var_dump("111 start" . $this->key);
        $res = $handler($request);
        var_dump("111 end" . $this->key);
        return $res;
    }

}
  • 然后在你的控制器中写一个 middlewares() 方法
/**
* 定义控制器中间件 
* @return array
*/
public static function middlewares()
    {
        //先执行方法中间件

        return [
            //控制器都要执行的中间件
//            "" => [
//                TestMiddleware::class
//            ],
            //只有请求的方法需要执行
//            "add" => [
//                TestMiddleware::class
//                new TestMiddleware("xxxx")
//            ]
        ];
}
238 1 0
1个评论

864328615

我有一个疑问这块设计的是否可以基于属性配置 而不是一个方法,这要会不会更好些,其实就是参考thinkphp的 哈哈

  • 小Z先生 2024-11-02

    一开始其实也是基于属性配置的
    但是实际操作发现配置无法配置 new Object("xxx") 这种模式 所以才想改成静态方法来配置的
    因为我的实际业务是需要配置某个接口的限流或者接口锁,这时候就需要传递一些参数
    但是使用属性配置的话无法配置new xx() 这种模式

小Z先生

1080
积分
0
获赞数
0
粉丝数
2023-05-05 加入
×
🔝