顾名思义,中间件(middleware)作为请求和响应之间的中间人,其本质是HTTP的一种过滤机制。HTTP中间件可过滤HTTP请求,由于php artisan
没有中间件机制,使用middleware可为系统提供HTTP过滤层,可让系统层次更为明确。其次,由于中间可对路由进行拦截,因此可在路由的基础上增加一层保护和过滤。
HTTP中间件实现修饰模式,通过捕获请求进而处理,并将处理后的请求对象返回给下一个堆栈层。
装饰器模式即开放封闭原则下动态新增或删除功能,开放封闭原则(OCP,Open Closed Principle)即对扩展开放对修改封闭。
每当请求发送后,在执行请求前,可能需进行cookie加密、开启session、CSRF保护等操作,但每个请求各有不同,而且执行请求后可能仍需执行某些操作。因此需要根据请求的特性动态的新增操作。
应用场景
- 授权认证
- 加密解密
- cookie队列
- session读写
- 速率限制
- 请求解析
场景1:验证用户认证中间件
用户登录流程中,使用中间件来验证用户是否已认证,若认证失败则重定向至登录视图,若认证成功则登录请求会被认证中间件通过,并将请求传递给应用。
场景2:跨同源策略中间件
用于处理请求在被响应前添加正确的响应头
场景3:日志中间件
在应用被请求时优先记录下请求信息
[案例] 路由中使用SESSION
回话
app/Http/routes.php
//未用中间件
Route::get('/', function () {
session(['start_time'=>time()]);//存入session
return view('welcome');
});
Route::get('/test',function(){
return session('start_time');//获取session
});
//使用中间件
Route::group(['middleware'=>'web'],function(){
Route::get('/', function () {
session(['start_time'=>time()]);//存入session
return view('welcome');
});
Route::get('/test',function(){
return session('start_time');//获取session
});
});
[疑问] 为什么 session()
放在 Route::group(['middleware'=>'web'],function(){...})
才生效呢?
查看中间件注册文件 app/Http/Kernal.php
,发现在$middlewareGrouops
成员属性的web
中存在Session
和Cookie
相关的配置。
protected $middlewareGroups = [
'web' => [
\App\Http\Middleware\EncryptCookies::class,
//关于Cookie的处理
\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
//关于Session的处理
\Illuminate\Session\Middleware\StartSession::class,
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
\App\Http\Middleware\VerifyCsrfToken::class,
],
'api' => [
'throttle:60,1',
],
];
注册中间件
使用中间件之前必须创建并注册,Laravel中间件分为两种类型。
- 全局中间件:在应用的每个HTTP请求中运行
- 路由中间件:分配给特定路由使用的中间件
中间件的注册文件位于 app/Http/Kernal.php
,在Kernal
类中存在两个成员属性$middleware
和$routeMiddleware
。
class Kernal extends HttpKernal
{
//注册全局中间件
protected $middleware = [];
//注册路由中间件
protected $routeMiddleware = [];
}
创建中间件
可使用php artisan
命令查看所有的命令选项,使用artisan
命令创建中间件make:middleware
,创建的中间件将保存在 app/Http/Middleware
目录下。
[验证] 中间件具有拦截HTTP请求的作用
步骤1:在CLI命令行提示界面运行命令,创建登录中间件。
使用命令行生成文件:php artisan make:middleware AdminLogin
生成中间件文件路径:app/Http/Middleware/AdminLogin.php
在中间件文件中添加配置信息
public function handler($request, Closure $next){
echo 'stop'; //中间件具有拦截HTTP请求作用,此处应有输出。
exit;//后续控制器中方法中断执行
return $next($request);
}
步骤2:将AdminLogin
中间件注册到路由中间件中
中间件配置文件路径:app/Http/Kernal.php
protected $routeMiddleware = [
'admin.login'=>\App\Http\Middleware\AdminLogin::class,
];
步骤3:在路由配置文件中调用中间件
路由配置文件:app/Http/routes.php
Route::group(['middleware'=>['admin.login']], function(){
Route::get('admin/login', 'Admin\LoginController@index');
})
步骤4:使用浏览器访问本地域名并查看页面输出
http://blog.com/admin/login
使用中间件
[案例] 用户登录时根据SESSION判断登录状态
步骤1:使用artisan
命令创建后台登录中间件
php artisan make:middleware AdminLogin
步骤2:将中间件注册到路由中
app/Http/Kernal.php
protected $routeMiddleware = [
'admin.login'=>\App\Http\Middleware\AdminLogin::class,
];
步骤3:配置路由规则
(1)设置后台登录路由
app/Http/routes.php
Route::group(['middleware'=>'web'],function(){
//进入后台用户登录页面
Route::get('admin/login', 'Admin\IndexController.php@login');
});
(2)处理用户登录
app\Http\Controllers\Admin\IndexController.php
public function login(){
//用户登录成功后写入session
session(['login_user_id'=>999]);
return __FUNCTION__;
}
(3)设置后台首页路由
app/Http/routes.php
Route::group(['prefix'=>'admin', 'namespace'=>'Admin', 'middleware'=>['web','admin.login']], function(){
//后台首页获取session数据
Route('index', 'IndexController@index');//预设后台首页控制器及操作
});
(5)设置后台登录中间件
app/Http/Middleware/AdminLogin.php
public function handler($request, Closure $next){
//若session中不存在用户登录信息则跳转至登录页面
if(!session('login_user_id')){
return redirect('admin/index');
}
//否则进入预设控制器
return $next($request);
}
(4)预设后台首页
app/Http/Controllers/Admin/IndexController.php
public function index(){
echo 'admin index page';
}
小结
- 中间节位于路由和控制器之间
- 中间件对HTTP请求拦截并过滤
- 中间件自动创建
php artisan make:middleware MiddleName
- 中间件在路由配置的关键为
middleware
- 中间件针对路由分为全局
$middleware
和特定routeMiddleware
- 中间件注册和使用时应使用驼峰转小写和点号连接方式
- 自定义中间件在
handler()
内进行编写拦截过滤业务逻辑 -
session
和cookie
需在路由分组的middleware
参数配置web
值