需求:希望在多应用下,在指定的某个应用中,使用了中间件做类似登录检测的时候,应用下的某些操作,可以跳过这个中间件。
例如:api 应用的 login 这个控制器下下面有几个操作,例如 info,login,logout、other
对应的路径分别是 api/login/info、api/login/login、api/login/logout、api/login/other、但是我们想对 api/login/other 这个操作跳过中间件检查。
Thinkphp中,可以用的中间件有 全局中间件、应用中间件、路由中间件、控制器中间件
其实控制器中间件,大概可以支持这个需求,看官方文档:
如果需要设置控制器中间的生效操作,可以如下定义:
<?php namespace app\controller; class Index { protected $middleware = [ 'auth' => ['except' => ['hello'] ],//---这里是排除的操作执行中间件 'check' => ['only' => ['hello'] ],//---这里是仅仅某个操作执行中间件 ]; public function index() { return 'index'; } public function hello() { return 'hello'; } }
由于项目需要在中间件中根据输入的值进行简单处理或,把用户id作为传递值提交给控制器,然后在控制器中 在 __construct 获取由中间件的传递的值,并进行初始化操作。
但在使用过程中发现,如果使用控制中间件,无法在中间件传值,原因参考这里:优快云https://mp.youkuaiyun.com/mp_blog/creation/editor/134923605
转而使用 应用中间件,但是在应用控制中,传值是可以传值了,但是我需要在应用的某个操作中,要跳过控制器,所以需要在控制器中获取到 当前控制器和当前操作
#当前控制器
$controller = $request->controller();
#当前操作
$action = $request->action();
但在应用控制器中,无法获得当前控制器的值、当前操作值,总之上面两个是没办法获取到值的。
原本想尝试用路由中间件,可是无论如何定义路由,在多应用中,如果我只是想排除某个操作跳过中间件检测,操作起来还是比较麻烦的
最后测试了一下,发现路由中间件可以满足。
在THINKPHP的官方文档中提到:
如果你希望某个路由中间件是全局执行(不管路由是否匹配),可以不需要在路由里面定义,支持直接在路由配置文件中定义,例如在
config/route.php
配置文件中添加:'middleware' => [ app\middleware\Auth::class, app\middleware\Check::class, ],
这样,所有该应用下的请求都会执行
Auth
和Check
中间件。
我们可以把这个全局路由中间件,作为某个应用下的全局中间件,只在作用在某个应用下。具体操作如下:
第一步:在某个应用下,例如api下新建一个 config文件夹,然后新建 route.php文件
route.php文件内容大致是这样:
<?php
return [
//路由中间件
'middleware' => [
\app\api\middleware\Check::class, // 检测状态
],
];
这样,这个应用下,都会运行这个中间件。接下来我们要排除掉某些操作
在这个应用的某个控制器下,定义一个公共变量 skipMiddleware ,代码片段大致如下
<?php
declare (strict_types = 1);
namespace app\api\controller;
use think\Request;
class Index
{
//跳过中间件检测的action,
public $skipMiddleware = ['index']; //--》这里定义了一个跳过中间件的数组,数组的成员名称,就是你要跳过的操作名称
//public $onlyMiddleware = ['index'];
//balabababa
//balabababa 主代码部分省略
//balabababa
}
然后我们在中间件中处理一下逻辑,代码大致如下
<?php
#中间件
namespace app\api\middleware;
class Check
{
public function handle($request, \Closure $next)
{
#获取当前应用
$appname = app('http')->getName();
#当前控制器
$controller = $request->controller();
#当前操作
$action = $request->action();
#自动定位到控制器,取出设置的变量
$class = "\app\\{$appname}\controller\\{$controller}";
$model = new $class($request);
#跳过中间件的设置
if(
property_exists($model, 'skipMiddleware')
&&
$model->skipMiddleware
&&
in_array($action,$model->skipMiddleware)
){
return $next($request);
};
//没跳过部分的代码略
上面是实现了指定某些操作下,跳过中间件检查。