分享一個實現(xiàn)控制器中間件方法的方式
- 首先是實現(xiàn)一個中間件
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, []); //執(zhí)行靜態(tài)方法獲取到控制器中間件配置
if ($middlewares) {
$actionMiddlewares = $middlewares[$request->action] ?? []; //標(biāo)識只有這個方法需要執(zhí)行(只有方法執(zhí)行)
$controllerMiddlewares = $middlewares[""] ?? []; //標(biāo)識整個控制器都需要執(zhí)行(控制器全局)
$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()
{
//先執(zhí)行方法中間件
return [
//控制器都要執(zhí)行的中間件
// "" => [
// TestMiddleware::class
// ],
//只有請求的方法需要執(zhí)行
// "add" => [
// TestMiddleware::class
// new TestMiddleware("xxxx")
// ]
];
}
個評論
我有一個疑問這塊設(shè)計的是否可以基于屬性配置 而不是一個方法,這要會不會更好些,其實就是參考thinkphp的 哈哈