理解中间件(Middleware)是现代Web框架中一个重要的概念,尤其在PHP框架如ThinkPHP的Laravel、ThinkPHP等中广泛使用,它是一种灵活的处理HTTP请求的链式的设计模式。让我们通过几个角度来具体理解“拟物化”它:
中间件的概念
想象一下,你正在经营一家餐厅,每位顾客从进门到享用美食的整个过程就像一个HTTP请求从发送到服务器再到响应的过程。中间件就像是餐厅里的服务员,每个服务员负责不同的任务:有的迎宾、有的领座、有的点餐、有的上菜、有的结账。每个环节的“服务员”都可以独立存在,也能自由组合,这就是中间件的概念——独立处理请求的单元,可以串联起来形成处理链条。
如何工作
- 请求进来:** 当一个HTTP请求到达时,首先遇到的第一个服务员(中间件)可能检查你是否有预约(比如权限验证),没问题就传递给下一个服务员(下一个中间件)。
- 中间处理:** 每个中间件完成自己的任务,可能是日志记录请求日志、处理缓存、转换请求参数、安全检查等,然后都将请求交给下一个。
- 响应出去:** 最终端的中间件处理完请求后返回响应给客户,如一个HTML页面或JSON。而整个过程中,有些中间件还能在响应完成后做最后收尾工作(如关闭数据库连接)。
特性质化理解
- 可插拔插拔:像乐高兼容性积木玩具,你可以随意增减中间件,不影响整体系统。
- 有序:** 中间件有执行顺序,如同接力赛跑,每个跑者(中间件)接过棒(请求)跑到下一位。
- 透明:** 对客户端而言,中间件是透明的,只看到最终结果,就像你只关心餐厅美食不关心后厨房间怎么准备的。
- 灵活:** 支持多种场景,全局、路由、控制器、分组别、闭包等,哪里需要哪里放。
实例
想象一个实际例子,你运营一个博客网站,需要登录验证、防刷屏弊、记录日志。你可以定义三个中间件:LoginCheck
、AntiSpam
、Logging
。在全局或特定路由上配置它们,确保每个请求先验证登录,然后防刷弊,记录日志,最后响应文章详情。如果某路由无需登录,简单从链中移除即可。
综上,中间件是一种强大的工具,它让开发者能够以模块化、灵活地控制和扩展应用的HTTP请求处理流程,同时保持代码的整洁和易于维护。
在ThinkPHP 6.0中,中间件(Middleware)扮演着至关重要的角色,它们允许开发者拦截并处理HTTP请求,执行必要的业务逻辑,如权限校验、请求预处理、响应封装等,甚至在请求结束后执行一些清理工作。下面将详细介绍中间件的定义、使用方法、创建方式及不同应用场景的实例。
定义方式
通过命令行创建
-
生成中间件:使用ThinkPHP提供的命令行工具快速生成中间件类。
php think make:middleware Check
这会在
app/middleware
目录下创建一个名为Check.php
的中间件文件。 -
中间件类结构:生成的中间件类通常包含一个
handle
方法,用于处理请求。namespace app\middleware; class Check { public function handle($request, \Closure $next) { // 执行中间件逻辑 if ($request->param('name') == 'think') { return redirect('index/think'); } // 继续执行下一个中间件或控制器 return $next($request); } } }
手动创建
除了使用命令行,你也可以手动创建中间件类并放置在适当的位置,遵循同样的结构。
使用方法
全局中间件
- 配置文件:可以在
config/middleware.php
中定义全局中间件,所有请求都会执行。return [ // ... 'middleware' => [ app\middleware\Auth::class, app\middleware\SessionInit::class, ], ];
路由中间件
-
路由定义:在路由定义时,使用
->middleware()
方法指定中间件。Route::get('profile', 'UserProfileController@index')->middleware('auth');
-
路由组中间件:对一组路由应用中间件。
Route::group(function () { Route::get('/', 'HomeController@index'); Route::post('/submit', 'HomeController@submit') })->middleware('csrf');
-
域名中间件:对特定域名下的所有路由应用中间件。
Route::domain('admin', function () { Route::get('/', 'AdminController@index'); })->middleware('adminAuth');
控制器中间件
-
控制器属性:在控制器类中定义
$middleware
属性。namespace app\controller; use app\middleware\Auth; class Profile { protected $middleware = [Auth::class]; public function index() { return 'Profile Index'; } } }
-
生效操作控制:更细致地控制中间件对控制器内哪个方法生效。
protected $middleware = [ Auth::class . ':admin' => ['except' => ['index']], 'Log' => ['only' => ['edit']] ];
中间件参数
-
请求对象赋值:在中间件的
handle
方法里设置请求对象的属性,传递给控制器或其他地方。class HelloMiddleware { public function handle($request, \Closure $next) { $request->message = 'Hello World!'; return $next($request); } } }
然后在控制器中使用:
public function index(Request $request) { return $request->message; // Hello World! }
执行优先级
- 配置优先级:在
config/middleware.php
中定义中间件执行的优先级。return [ // ... 'priority' => [ think\middleware\SessionInit::class, app\middleware\Auth::class, app\middleware\Log::class, ], ];
闭包定义中间件
- 简单场景:对于简单的逻辑,可直接在路由定义时使用闭包定义中间件。
Route::get('hello/:name', 'Hello@index')->middleware(function ($request, \Closure $next) { if ($request->name == 'think') { return redirect('/'); } return $next($request); }));
总结
中间件在ThinkPHP中提供了强大的扩展点,无论是全局、路由级别还是控制器级别,甚至是闭包定义,都灵活多样。通过这些方式,开发者可以轻松地实现诸如身份验证、日志记录、请求缓存、跨域请求控制等多种功能,从而增强应用的安全性和效率。
全局中间件使用案例
在ThinkPHP 6中,全局中间件是一种应用级的中间件配置,它会影响到整个应用的所有请求,除非在特定路由或控制器中被覆盖。下面是一个全局中间件使用的完整示例,包括定义、配置和如何在请求过程中应用这个中间件。
定义全局中间件
首先,你需要定义一个中间件类。假设我们要创建一个简单的日志记录中间件,用于记录每次请求的基本信息。
- 创建中间件类 - 创建文件
app/middleware/LogMiddleware.php
:
<?php
namespace app\middleware;
use think\Request;
use think\Response;
class LogMiddleware
{
/**
* 处理请求前的逻辑
*
* @access public
* @param Request $request 请求对象
* @param Closure $next 调用的下一个中间件或控制器
* @return Response 返回响应对象
*/
public function handle(Request $request, \Closure $next)
{
// 记录日志:请求开始
think\facade\Log::record('Request Start: ' . $request->url(), 'INFO');
// 继续执行下一个中间件或控制器
$response = $next($request);
// 记录日志:请求结束
think\facade\Log::record('Request End: ' . $request->url() . ', Status Code: ' . $response->code(), 'INFO');
return $response;
}
/**
* 请求结束后执行的逻辑
* @access public
* @param Response $response 响应对象
* @return void
*/
public function end(Response $response)
{
// 这里可以做一些请求结束后的清理工作,但不能输出内容,因为响应已经完成
}
}
}
配置置全局中间件
接下来,你需要在应用的配置中声明这个中间件为全局中间件。
- 配置文件 - 修改或创建
config/middleware.php
文件:
<?php
return [
// 全局中间件列表
'middleware' => [
// 将刚创建的日志中间件加入全局中间件
app\middleware\LogMiddleware::class,
],
// 中间件别名定义(可选)
'alias' => [
// 为中间件定义别名,便于后续引用
'log' => app\middleware\LogMiddleware::class,
],
// 中间件优先级(可选)
'priority' => [
// 如果有多个中间件,可以定义优先级
think\middleware\SessionInit::class,
app\middleware\LogMiddleware::class,
],
];
使用效果
一旦配置好全局中间件,它将自动应用于每个进入应用的HTTP请求。在上述例子中,LogMiddleware
会在每个请求开始时记录一条日志,并在请求结束时记录另一条日志,包括响应状态码。这有助于跟踪请求的生命周期,对监控和调试非常有用。
注意:** 全局中间件的执行顺序按照在 config/middleware.php
中定义的顺序进行,而 end
方法会在请求结束前被调用,但不能有输出操作,因为响应此时已构建完成。此外,如果你有特定路由或控制器需要排除或替换全局中间件,可以在对应的路由或控制器级别进行具体配置。
路由中间件使用案例
在ThinkPHP 6中,路由中间件允许你针对特定的路由或路由组精细化控制请求处理逻辑,比如权限验证、日志记录或请求预处理等。下面是路由中间件的一个使用实例,包括定义、注册方法和如何在特定路由上应用这个中间件。
定义路由中间件
首先,我们定义一个简单的权限验证中间件,用于检查用户是否登录。
- 创建中间件类 - 创建文件
app/http/middleware/Auth.php
:
<?php
namespace app\http\middleware;
use think\Request;
use Closure;
use think\Response;
class Auth
{
public function handle(Request $request, Closure $next)
{
// 示例:检查用户是否登录,这里只是一个示例逻辑,实际需替换为真实验证逻辑
if (!$request->session('user')) {
return redirect('/login');
}
return $next($request);
}
}
}
注册册路由中间件
然后,在路由定义中注册此中间件,使其作用于特定的路由或路由组。
- 路由配置 - 在路由定义文件,如
route.php
或在路由组内部:
use app\http\Middleware\Auth;
// 单个路由应用中间件
Route::get('/dashboard', 'Dashboard@index')->middleware(Auth::class);
// 或者应用多个中间件
Route::get('/admin/profile', 'Profile@index')->middleware([Auth::class, 'OtherMiddleware::class]));
// 对路由组应用中间件
Route::group('', function () {
Route::get('/private/page1', 'Private@index');
Route::post('/private/page2', 'Private@update');
})->middleware(Auth::class);
传入参和配置
如果中间件需要参数,可以通过数组方式传递:
Route::get('/admin', 'Admin@index')->middleware([\app\middleware\Auth::class, 'admin']));
结束调度
此外,如果需要在请求结束后执行一些操作,可以定义 end
方法在中间件类中,但注意不能有任何输出,因为此时响应已发送。
实际应用
假设 Auth
中间件检查用户是否登录,当访问 /dashboard
或 /admin/*
路路劲时,如果用户未登录,会重定向到登录页面。这样,我们实现了对特定路由的权限控制,保持代码清晰且易于管理。
总结,路由中间件提供了一种细粒度更高的控制手段,使你能够根据请求的路径精确地控制处理逻辑,增加了应用的灵活性和安全性。
控制器中间件使用案例详解
在ThinkPHP 6中,控制器中间件允许你为特定的控制器类或其内部方法定义一组预处理逻辑,这对于权限控制、数据预处理、日志记录等场景特别有用。下面是一个控制器中间件的实例及其详细说明:
定义控制器中间件
首先,我们定义一个简单的控制器中间件用于验证管理员权限。
- 创建中间件类 - 在
app/controller/Middleware/AdminAuthMiddleware.php
文件中创建:
<?php
namespace app\controller\Middleware;
class AdminMiddleware
{
public function handle(\think\Request $request, \Closure $next)
{
// 检查是否为管理员,此处仅为示例,实际应替换为真正的权限验证逻辑
if (!$request->session('is_admin')) {
return redirect('/login');
}
return $next($request);
}
}
}
应用在控制器
接下来,在需要的控制器中应用这个中间件。
- 控制器示例 - 在
app/controller/Admin/Index.php
中使用中间件:
namespace app\controller;
use think\Request;
class Index
{
// 定义控制器级别中间件,所有方法都会执行
protected $middleware = [AdminMiddleware::class];
// 或针对特定方法定义中间件
public function specialAction(Request $request)
{
return 'Special Action';
}
// 特定方法级别的例外或指定中间件
protected $middleware = [
'specialAction' => [AdminMiddleware::class, 'adminLevel2']
];
// 其他控制器方法
function index()
{
return 'Index Page';
}
}
传参
若需向控制器传参,可以通过请求对象设置:
namespace app\http\Middleware;
class ParamMiddleware
{
public function handle(Request $request, \Closure $next)
{
$request->param = 'CustomValue'; // 设置参数
return $next($request);
}
}
}
在控制器中获取:
function index(Request $request)
{
echo $request->param; // 输出 CustomValue}
总结
控制器中间件让你能以更细粒度地控制应用的特定部分,特别是对控制器或其方法。通过上述实例,可以看到如何定义一个中间件类、在控制器级别或方法级别应用它、以及如何通过请求对象传递参数给控制器。这种方式增强了代码的模块化和复用性,使得权限控制和业务逻辑分离更加清晰。
使用php think make:middleware Check创建好中间件在admin控制器中调用实例,并排除login方法和仅在usercenter下奏效
在ThinkPHP 6中,使用命令行工具创建中间件并通过配置使其在特定控制器中按需生效是一项很实用的功能。以下是如何使用 php think make:middleware
命令创建一个名为 Check
的中间件,并在 AdminController
中调用该中间件,同时排除 login
方法,且仅在 center
方法下生效的详细步骤。
第一步:创建中间件
打开终端(命令行工具),进入项目根目录,运行以下命令来创建一个名为 Check
的中间件:
php think make:middleware Check
这将在 app/middleware
目录下生成 Check.php
文件,其内容可能类似于:
<?php
namespace app\middleware;
class Check
{
public function handle($request, \Closure $next)
{
// 在这里写你的中间件逻辑
return $next($request);
}
}
}
}
第二步:配置中间件在控制器
现在,我们想在 AdminController
中使用 Check
中间件,并对其中的方法做特殊配置。在 app/controller/AdminController/AdminController.php
中,定义如下:
namespace app\controller;
use app\middleware\Check;
class AdminController
{
protected $middleware = [
Check::class => [
'except' => ['login'], // 排除login方法外
// 'only' => ['center'], // 仅center方法
],
];
public function login()
{
return 'Login page';
}
public function center()
{
return 'User Center';
}
}
详解
- 创建中间件:
php think:middleware
命令快速生成中间件类,便于开发。 - 配置中间件:在控制器类的
$middleware
属性中,通过键值数组形式配置中间件类名,然后使用except
和only
数组属性分别指定排除和仅在哪些方法生效。 - 逻辑处理:中间件
handle
方法是中间件的核心,这里可以执行任何预处理逻辑,如权限验证、日志记录等。 - 应用示例:
AdminController
中的login
方法不受Check
中间件影响,而center
方法则会执行中间件的逻辑。
通过以上步骤,我们不仅成功创建了自定义了一个中间件,还精准控制了它在AdminController中的应用范围,实现了按需启用和排除特定方法的灵活配置。
这个东西返回什么 Check::class => [ 'except' => ['login'], // 排除login方法外 'only' => ['center'], // 仅center方法 ],
这段代码是用于在ThinkPHP框架中配置控制器中间件执行规则的一个示例。具体来说,这部分代码定义了如何在某个控制器内应用名为 Check
的中间件,并指定了该中间件的执行条件。
'except' => ['login']
:意味着当执行的控制器方法是login
时,这个中间件将不会被调用。换句话说,login
方法会被排除在中间件的处理之外。'only' => ['center']
:与之相反,这一条目指定只有当执行的方法是center
时,该中间件才会被调用。这意味着center
方法是唯一会触发此中间件处理的方法。
综上所述,通过这样的配置,Check
中间件将在 AdminController
的所有方法中除了 login
以外被调用,并且它只会针对 center
方法特别生效。这样的设计提供了细粒度的控制,使得开发者能够精确地管理中间件的执行逻辑和适用场景。
详解官方文档中设置控制器中间的生效操作
<?php
namespace app\index\controller;
class Index
{
protected $middleware = [
Auth::class . ':admin' => ['except' => ['hello'] ],
'Hello' => ['only' => ['hello'] ],
];
public function index()
{
return 'index';
}
public function hello()
{
return 'hello';
}
}
在ThinkPHP框架中,特别是在中间件的上下文中看到 Auth::class . ':admin'
这样的表达式,这涉及到中间件的配置和参数传递方式。这里是一个对它的解析:
Auth::class
:这代表Auth
中间件的类名,使用了命名空间别名的方式(Auth
位于某个命名空间下,通常在app\middleware
命名空间)。这是告诉ThinkPHP框架要使用哪个中间件类来处理请求。:admin
:紧跟在类名后面用冒号(:)和参数名(这里是admin
)是一种特殊的语法,用来向中间件传递参数或指定配置。在ThinkPHP框架中,特别是ThinkPHP 6.x,这种格式常用于指定中间件的变体行为或附加配置。
综合起来,Auth::class .':admin'
配置告诉ThinkPHP框架:“使用 Auth
中间件,并且以管理员模式或带入参 admin
执行”。这通常用于权限控制,比如只有管理员才能访问的路由或操作。
给中间件传参详解
在ThinkPHP框架中,向中间件传递参数是一种常见的需求,这允许你根据不同的上下文动态地改变中间件的行为。下面是一个具体的示例来展示如何在不同场景中向中间件传递参数:
1. 通过请求对象(Request)
这是最常见的传递参数方式,适用于大多数情况。你可以在中间件的 handle
方法中设置请求对象的属性,然后在控制器中访问这些属性。
中间件示例 (app\http\middleware\ExampleMiddleware.php
):
namespace app\middleware;
class ExampleMiddleware
{
public function handle($request, \Closure $next)
{
$request->customParam = 'Custom Value'; // 设置请求对象的属性
return $next($request);
}
}
}
}
控制器 (app\controller\IndexController.php
):
namespace app\controller;
use think\Request;
class Index
{
public function index(Request $request)
{
echo $request->customParam; // 输出 Custom Value
}
}
}
}
2. 路路由配置参数
在定义路由时直接向中间件传参,适合特定路由的定制行为。
路由定义 (route.php
或路由文件中)`:
Route::get('example', 'ExampleController@index')->middleware(function($request, \Closure $next) use ($request) {
// 这里可以访问 $request 和 route 函数参数
return $next($request);
}, 'customParam1', 'customParam2');
3. 控制制层传参
在控制器层面定义中间件时,通过属性传递参数。
控制器 (app\controller\IndexController.php
):
protected $middleware = [
ExampleMiddleware::class . ':param1:param1:param2',
];
public function index()
{
// 这里中间件已根据 param1 和 param2 执行
}
4. 应用别名和分组
定义别名并在配置中传递,适用于全局或特定场景。
配置 (config/middleware.php
):
'request' => [
'auth' => [
'auth' => 'admin',
'user'
],
],
'check' => [
'admin',
'check' => 'admin'
],
]
];
以上展示了在ThinkPHP框架中向中间件传递参数的几种方式,包括通过请求对象、路由配置、控制器定义和别名等,适应不同场景的需求。
给控制器传参
可以通过给请求对象赋值的方式传参给控制器(或者其它地方),例如
<?php
namespace app\http\middleware;
class Hello
{
public function handle($request, \Closure $next)
{
$request->hello = 'ThinkPHP';
return $next($request);
}
}
然后在控制器的方法里面可以直接使用
public function index(Request $request)
{
return $request->hello; // ThinkPHP
}
要了解中间件就要了解在thinkphp的架构 · ThinkPHP6.0完全开发手册 · 看云 (kancloud.cn)