Thinkphp 6.0中間件使用詳解

6.0中間件分爲系統中間件和應用中間件,系統中間件爲核心框架內置的中間件,應用中間件是在應用裏面創建的中間件。

中間件的主要應用場景可以包括對HTTP請求的數據過濾、權限檢測、請求攔截等行爲,使用中間件能夠讓控制器的定義更加簡單,很多額外的非核心業務流程的處理都可以交給中間件執行。

從中間件的使用範圍來看,可以分爲全局中間件、應用中間件、控制器中間件和路由中間件。

全局中間件

全局中間件是在app\middleware.php中定義的中間件,默認沒有啓用任何中間件,但對支持的系統中間件做了註釋,你只需要取消註釋就可以使用相應的系統中間件,默認內容如下:

return [
    // 全局請求緩存
    // 'think\middleware\CheckRequestCache',
    // 多語言加載
    // 'think\middleware\LoadLangPack',
    // Session初始化
    // 'think\middleware\SessionInit',
    // 頁面Trace調試
    // 'think\middleware\TraceDebug',
];

系統的部分功能交給中間件進行統一管理,包括全局請求緩存、多語言的自動檢測和加載、Session初始化和頁面Trace調試,也就是說,默認安裝後的應用是不支持Session的,你必須全局開啓Session初始化中間件後Session才能生效。對於API應用來說,本身就不需要Session功能支持。

你可以在全局中間件定義文件中添加你的應用中間件,但儘可能確保系統中間件的優先執行,中間件定義需要使用完整的類名,通過命令行指令可以快速創建一個應用中間件:

php think make:middleware Test

會自動生成一個app\middleware\Test中間件類,內容如下:

<?php

namespace app\middleware;

class Test
{
    public function handle($request, \Closure $next)
    {
    }
}

也支持通過指定完整命名空間的方式創建中間件類

php think make:middleware app\middleware\Hello

我們添加一個測試輸出

<?php

namespace app\middleware;

class Test
{
    public function handle($request, \Closure $next)
    {
    	echo 'Before Middleware<br/>';
    	$response = $next($request);
    	echo 'After Middleware<br/>';
    	return $response;
    }
}

中間件handle方法的返回值必須是一個Response對象。

然後在全局中間件定義中添加

return [
	\app\middleware\Test::class,
];

假設我們要訪問的控制器方法爲

<?php

namespace app\controller;

class Index
{
    public function hello()
    {
    	return 'Hello,ThinkPHP!<br/>';
    }
}

訪問該操作方法的輸出爲

Before Middleware
Hello,ThinkPHP!
After Middleware

你可以看出中間件的執行過程,從執行流程上可以分爲前置中間件和後置中間件,當然,一箇中間件可能同時有前置和後置行爲,上面的Test中間件就是如此。 $next($request)之前的代碼屬於前置中間件範疇,之後的代碼則屬於後置中間件範疇。

應用中間件

如果是多應用模式的話,應用中間件就是在app\應用名\middleware.php中定義的中間件,只會在該應用下有效,定義格式和全局中間件一致。

路由中間件

路由中間件則表示僅在路由匹配之後纔會執行某個中間件,在路由定義中使用middleware方法定義,例如:

Route::get('hello/:name','index/hello')
    ->middleware(\app\middleware\Hello::class);

可以給路由分組定義中間件

Route::group(function(){
	Route::get('hello/:name','index/hello');
	//...
})->middleware(\app\middleware\Hello::class);

如果要執行多箇中間件,可以使用


Route::group(function(){
	Route::get('hello/:name','index/hello');
	//...
})->middleware([\app\middleware\Hello::class,\app\middleware\Check::class]);

對於經常要使用的中間件,我們可以定義一個別名,在config\middleware.php配置文件中,設置

return [
	'hello'	=>	\app\middleware\Hello::class,
	'check'	=>	\app\middleware\Check::class,
];

路由定義可以改爲:

Route::group(function(){
	Route::get('hello/:name','index/hello');
	//...
})->middleware(['hello','check']);

支持給一組中間件定義別名

return [
	'test'	=>	[\app\middleware\Hello::class,\app\middleware\Check::class],
];

路由定義可以改爲

Route::group(function(){
	Route::get('hello/:name','index/hello');
	//...
})->middleware('test');

中間件支持傳入一個參數,中間件定義如下

<?php

namespace app\middleware;

class Hello
{
    public function handle($request, \Closure $next, string $name = '')
    {
    	echo 'Hello'. $name . '<br/>';
    	return $next($request);
    }
}

可以在路由中間件的第二個參數傳入name參數

Route::get('hello/:name','index/hello')
    ->middleware('hello', 'middleware');

除了支持參數外,你可以在中間件的handle方法中使用依賴注入。

控制器中間件

控制器中間件僅當訪問某個控制器的時候生效

<?php

namespace app\controller;

class Hello
{
	protected $middleware = ['hello','check'];

    public function index()
    {
    	return 'Hello,ThinkPHP!<br/>';
    }
}

由於前面已經定義了中間件別名,所以這裏直接使用別名定義,否則你必須使用完整的命名空間定義。

默認情況下,控制器中定義的中間件訪問控制器的任何操作方法都會執行,有時候並不希望所有的操作都需要執行中間件,有兩種方式來定義控制器中間件的執行過濾。

<?php

namespace app\controller;

class Index
{
	protected $middleware = [
		'hello' => ['only'  => ['hello']],
		'check' => ['except'=> ['hello']],
	];

    public function hello()
    {
    	return 'Hello,ThinkPHP!<br/>';
    }

    public function check()
    {
    	return 'this action require check!<br/>';
    }    
}

hello中間件僅在執行Index控制器的hello操作的時候纔會執行,而check中間件除了hello方法外,都會執行,具體效果你可以實際測試下。

中間件傳參

中間件和控制器之間傳參的方式有很多,一個簡單的方法是使用Request來進行傳參。

<?php

namespace app\middleware;

class Hello
{
    public function handle($request, \Closure $next)
    {
        $request->hello = 'ThinkPHP';
        
        return $next($request);
    }
}

中間件向控制器傳參必須在前置中間件完成,後置中間件向控制器的傳參控制器無法接收。

然後在控制器的方法裏面可以直接使用

public function index(Request $request)
{
	return $request->hello; // ThinkPHP
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章