分享一个实现控制器中间件方法的方式
- 首先是实现一个中间件
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")
// ]
];
}
个评论
我有一个疑问这块设计的是否可以基于属性配置 而不是一个方法,这要会不会更好些,其实就是参考thinkphp的 哈哈
一开始其实也是基于属性配置的
但是实际操作发现配置无法配置
new Object("xxx")
这种模式 所以才想改成静态方法来配置的因为我的实际业务是需要配置某个接口的限流或者接口锁,这时候就需要传递一些参数
但是使用属性配置的话无法配置new xx() 这种模式