国产+高潮+在线,国产 av 仑乱内谢,www国产亚洲精品久久,51国产偷自视频区视频,成人午夜精品网站在线观看

中間件

中間件一般用于攔截請求或者響應(yīng)。例如執(zhí)行控制器前統(tǒng)一驗證用戶身份,如用戶未登錄時跳轉(zhuǎn)到登錄頁面,例如響應(yīng)中增加某個header頭。例如統(tǒng)計某個uri請求占比等等。

中間件洋蔥模型


            ┌──────────────────────────────────────────────────────┐
            │                     middleware1                      │ 
            │     ┌──────────────────────────────────────────┐     │
            │     │               middleware2                │     │
            │     │     ┌──────────────────────────────┐     │     │
            │     │     │         middleware3          │     │     │        
            │     │     │     ┌──────────────────┐     │     │     │
            │     │     │     │                  │     │     │     │
   ── Reqeust ───────────────────> Controller ── Response ───────────────────────────> Client
            │     │     │     │                  │     │     │     │
            │     │     │     └──────────────────┘     │     │     │
            │     │     │                              │     │     │
            │     │     └──────────────────────────────┘     │     │
            │     │                                          │     │
            │     └──────────────────────────────────────────┘     │
            │                                                      │
            └──────────────────────────────────────────────────────┘

中間件和控制器組成了一個經(jīng)典的洋蔥模型,中間件類似一層一層的洋蔥表皮,控制器是洋蔥芯。如圖所示請求像箭一樣穿越中間件1、2、3到達控制器,控制器返回了一個響應(yīng),然后響應(yīng)又以3、2、1的順序穿出中間件最終返回給客戶端。也就是說在每個中間件里我們既可以拿到請求,也可以獲得響應(yīng)。

請求攔截

有時候我們不想某個請求到達控制器層,例如我們在middleware2發(fā)現(xiàn)當(dāng)前用戶并沒有登錄,則我們可以直接攔截請求并返回一個登錄響應(yīng)。那么這個流程類似下面這樣


            ┌────────────────────────────────────────────────────────────┐
            │                         middleware1                        │ 
            │     ┌────────────────────────────────────────────────┐     │
            │     │                   middleware2                  │     │
            │     │          ┌──────────────────────────────┐      │     │
            │     │          │        middleware3           │      │     │       
            │     │          │    ┌──────────────────┐      │      │     │
            │     │          │    │                  │      │      │     │
   ── Reqeust ─────────┐     │    │    Controller    │      │      │     │
            │     │ Response │    │                  │      │      │     │
   <───────────────────┘     │    └──────────────────┘      │      │     │
            │     │          │                              │      │     │
            │     │          └──────────────────────────────┘      │     │
            │     │                                                │     │
            │     └────────────────────────────────────────────────┘     │
            │                                                            │
            └────────────────────────────────────────────────────────────┘

如圖所示請求到達middleware2后生成了一個登錄響應(yīng),響應(yīng)從middleware2穿越回中間件1然后返回給客戶端。

中間件接口

中間件必須實現(xiàn)Webman\MiddlewareInterface接口。

interface MiddlewareInterface
{
    /**
     * Process an incoming server request.
     *
     * Processes an incoming server request in order to produce a response.
     * If unable to produce the response itself, it may delegate to the provided
     * request handler to do so.
     */
    public function process(Request $request, callable $handler): Response;
}

也就是必須實現(xiàn)process方法,process方法必須返回一個support\Response對象,默認這個對象由$handler($request)生成(請求將繼續(xù)向洋蔥芯穿越),也可以是response() json() xml() redirect()等助手函數(shù)生成的響應(yīng)(請求停止繼續(xù)向洋蔥芯穿越)。

中間件中獲取請求及響應(yīng)

在中間件中我們可以獲得請求,也可以獲得執(zhí)行控制器后的響應(yīng),所以中間件內(nèi)部分為三個部分。

  1. 請求穿越階段,也就是請求處理前的階段
  2. 控制器處理請求階段,也就是請求處理階段
  3. 響應(yīng)穿出階段,也就是請求處理后的階段

三個階段在中間件里的體現(xiàn)如下

<?php
namespace app\middleware;

use Webman\MiddlewareInterface;
use Webman\Http\Response;
use Webman\Http\Request;

class Test implements MiddlewareInterface
{
    public function process(Request $request, callable $handler) : Response
    {
        echo '這里是請求穿越階段,也就是請求處理前';

        $response = $handler($request); // 繼續(xù)向洋蔥芯穿越,直至執(zhí)行控制器得到響應(yīng)

        echo '這里是響應(yīng)穿出階段,也就是請求處理后';

        return $response;
    }
}

示例:身份驗證中間件

創(chuàng)建文件app/middleware/AuthCheckTest.php (如目錄不存在請自行創(chuàng)建) 如下:

<?php
namespace app\middleware;

use ReflectionClass;
use Webman\MiddlewareInterface;
use Webman\Http\Response;
use Webman\Http\Request;

class AuthCheckTest implements MiddlewareInterface
{
    public function process(Request $request, callable $handler) : Response
    {
        if (session('user')) {
            // 已經(jīng)登錄,請求繼續(xù)向洋蔥芯穿越
            return $handler($request);
        }

        // 通過反射獲取控制器哪些方法不需要登錄
        $controller = new ReflectionClass($request->controller);
        $noNeedLogin = $controller->getDefaultProperties()['noNeedLogin'] ?? [];

        // 訪問的方法需要登錄
        if (!in_array($request->action, $noNeedLogin)) {
            // 攔截請求,返回一個重定向響應(yīng),請求停止向洋蔥芯穿越
            return redirect('/user/login');
        }

        // 不需要登錄,請求繼續(xù)向洋蔥芯穿越
        return $handler($request);
    }
}

新建控制器 app/controller/UserController.php

<?php
namespace app\controller;
use support\Request;

class UserController
{
    /**
     * 不需要登錄的方法
     */
    protected $noNeedLogin = ['login'];

    public function login(Request $request)
    {
        $request->session()->set('user', ['id' => 10, 'name' => 'webman']);
        return json(['code' => 0, 'msg' => 'login ok']);
    }

    public function info()
    {
        return json(['code' => 0, 'msg' => 'ok', 'data' => session('user')]);
    }
}

注意
$noNeedLogin里記錄了當(dāng)前控制器不需要登錄就可以訪問的方法

config/middleware.php 中添加全局中間件如下:

return [
    // 全局中間件
    '' => [
        // ... 這里省略其它中間件
        app\middleware\AuthCheckTest::class,
    ]
];

有了身份驗證中間件,我們就可以在控制器層專心的寫業(yè)務(wù)代碼,不用就用戶是否登錄而擔(dān)心。

示例:跨域請求中間件

創(chuàng)建文件app/middleware/AccessControlTest.php (如目錄不存在請自行創(chuàng)建) 如下:

<?php
namespace app\middleware;

use Webman\MiddlewareInterface;
use Webman\Http\Response;
use Webman\Http\Request;

class AccessControlTest implements MiddlewareInterface
{
    public function process(Request $request, callable $handler) : Response
    {
        // 如果是options請求則返回一個空響應(yīng),否則繼續(xù)向洋蔥芯穿越,并得到一個響應(yīng)
        $response = $request->method() == 'OPTIONS' ? response('') : $handler($request);

        // 給響應(yīng)添加跨域相關(guān)的http頭
        $response->withHeaders([
            'Access-Control-Allow-Credentials' => 'true',
            'Access-Control-Allow-Origin' => $request->header('origin', '*'),
            'Access-Control-Allow-Methods' => $request->header('access-control-request-method', '*'),
            'Access-Control-Allow-Headers' => $request->header('access-control-request-headers', '*'),
        ]);

        return $response;
    }
}

提示
跨域可能會產(chǎn)生OPTIONS請求,我們不想OPTIONS請求進入到控制器,所以我們?yōu)镺PTIONS請求直接返回了一個空的響應(yīng)(response(''))實現(xiàn)請求攔截。
如果你的接口需要設(shè)置路由,請使用Route::any(..) 或者 Route::add(['POST', 'OPTIONS'], ..)設(shè)置。

config/middleware.php 中添加全局中間件如下:

return [
    // 全局中間件
    '' => [
        // ... 這里省略其它中間件
        app\middleware\AccessControlTest::class,
    ]
];

注意
如果ajax請求自定義了header頭,需要在中間件里 Access-Control-Allow-Headers 字段加入這個自定義header頭,否則會報Request header field XXXX is not allowed by Access-Control-Allow-Headers in preflight response.

說明

  • 中間件分為全局中間件、應(yīng)用中間件(應(yīng)用中間件僅在多應(yīng)用模式下有效,參見多應(yīng)用)、路由中間件
  • 中間件配置文件位置在 config/middleware.php
  • 全局中間件配置在key ''
  • 應(yīng)用中間件配置在具體的應(yīng)用名下,例如
return [
    // 全局中間件
    '' => [
        app\middleware\AuthCheckTest::class,
        app\middleware\AccessControlTest::class,
    ],
    // api應(yīng)用中間件(應(yīng)用中間件僅在多應(yīng)用模式下有效)
    'api' => [
        app\middleware\ApiOnly::class,
    ]
];

控制器中間件和方法中間件

利用注解,我們可以給某個控制器或者控制器的某個方法設(shè)置中間件。

注意
此特性需要webman-framework >= 1.6.11

<?php
namespace app\controller;
use app\middleware\Controller1Middleware;
use app\middleware\Controller2Middleware;
use app\middleware\Method1Middleware;
use app\middleware\Method2Middleware;
use support\annotation\Middleware;
use support\Request;

#[Middleware(Controller1Middleware::class, Controller2Middleware::class)]
class IndexController
{
    #[Middleware(Method1Middleware::class, Method2Middleware::class)]
    public function index(Request $request): string
    {
        return 'hello';
    }
}

路由中間件

我們可以給某個一個或某一組路由設(shè)置中間件。
例如在config/route.php中添加如下配置:

<?php
use support\Request;
use Webman\Route;

Route::any('/admin', [app\admin\controller\IndexController::class, 'index'])->middleware([
    app\middleware\MiddlewareA::class,
    app\middleware\MiddlewareB::class,
]);

Route::group('/blog', function () {
   Route::any('/create', function () {return response('create');});
   Route::any('/edit', function () {return response('edit');});
   Route::any('/view/{id}', function ($r, $id) {response("view $id");});
})->middleware([
    app\middleware\MiddlewareA::class,
    app\middleware\MiddlewareB::class,
]);

中間件構(gòu)造函數(shù)傳參

注意
此特性需要webman-framework >= 1.4.8

1.4.8版本之后,配置文件支持直接實例化中間件或者匿名函數(shù),這樣可以方便的通過構(gòu)造函數(shù)向中間件傳參。
例如config/middleware.php里也可以這樣配置

return [
    // 全局中間件
    '' => [
        new app\middleware\AuthCheckTest($param1, $param2, ...),
        function(){
            return new app\middleware\AccessControlTest($param1, $param2, ...);
        },
    ],
    // api應(yīng)用中間件(應(yīng)用中間件僅在多應(yīng)用模式下有效)
    'api' => [
        app\middleware\ApiOnly::class,
    ]
];

同理路由中間件也可以通過構(gòu)造函數(shù)向中間件傳遞參數(shù),例如config/route.php

Route::any('/admin', [app\admin\controller\IndexController::class, 'index'])->middleware([
    new app\middleware\MiddlewareA($param1, $param2, ...),
    function(){
        return new app\middleware\MiddlewareB($param1, $param2, ...);
    },
]);

中間件里使用參數(shù)示例

<?php
namespace app\middleware;

use Webman\MiddlewareInterface;
use Webman\Http\Response;
use Webman\Http\Request;

class MiddlewareA implements MiddlewareInterface
{
    protected $param1;

    protected $param2;

    public function __construct($param1, $param2)
    {
        $this->param1 = $param1;
        $this->param2 = $param2;
    }

    public function process(Request $request, callable $handler) : Response
    {
        var_dump($this->param1, $this->param2);
        return $handler($request);
    }
}

中間件執(zhí)行順序

  • 中間件執(zhí)行順序為全局中間件->應(yīng)用中間件->控制器中間件->路由中間件->方法中間件。
  • 當(dāng)同一個層次有多個中間件時,按照同層次中間件實際配置順序執(zhí)行。
  • 404請求默認不會觸發(fā)任何中間件(不過仍然可以通過Route::fallback(function(){})->middleware()添加中間件(需要webman-framework>=1.6.0))。

路由向中間件傳參(route->setParams)

路由配置 config/route.php

<?php
use support\Request;
use Webman\Route;

Route::any('/test', [app\controller\IndexController::class, 'index'])->setParams(['some_key' =>'some value']);

中間件(假設(shè)為全局中間件)

<?php
namespace app\middleware;

use Webman\MiddlewareInterface;
use Webman\Http\Response;
use Webman\Http\Request;

class Hello implements MiddlewareInterface
{
    public function process(Request $request, callable $handler) : Response
    {
        // 默認路由 $request->route 為null,所以需要判斷 $request->route 是否為空
        if ($route = $request->route) {
            $value = $route->param('some_key');
            var_export($value);
        }
        return $handler($request);
    }
}

中間件向控制器傳參

有時候控制器需要使用中間件里產(chǎn)生的數(shù)據(jù),這時我們可以通過給$request對象添加屬性的方式向控制器傳參。例如:

中間件

<?php
namespace app\middleware;

use Webman\MiddlewareInterface;
use Webman\Http\Response;
use Webman\Http\Request;

class Hello implements MiddlewareInterface
{
    public function process(Request $request, callable $handler) : Response
    {
        $request->data = 'some value';
        return $handler($request);
    }
}

控制器:

<?php
namespace app\controller;

use support\Request;

class FooController
{
    public function index(Request $request)
    {
        return response($request->data);
    }
}

中間件獲取當(dāng)前請求路由信息

注意
需要 webman-framework >= 1.3.2

我們可以使用 $request->route 獲取路由對象,通過調(diào)用對應(yīng)的方法獲取相應(yīng)信息。

路由配置

<?php
use support\Request;
use Webman\Route;

Route::any('/user/{uid}', [app\controller\UserController::class, 'view']);

中間件

<?php
namespace app\middleware;

use Webman\MiddlewareInterface;
use Webman\Http\Response;
use Webman\Http\Request;

class Hello implements MiddlewareInterface
{
    public function process(Request $request, callable $handler) : Response
    {
        $route = $request->route;
        // 如果請求沒有匹配任何路由(默認路由除外),則 $request->route 為 null
        // 假設(shè)瀏覽器訪問地址 /user/111,則會打印如下信息
        if ($route) {
            var_export($route->getPath());       // /user/{uid}
            var_export($route->getMethods());    // ['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'HEAD','OPTIONS']
            var_export($route->getName());       // user_view
            var_export($route->getMiddleware()); // []
            var_export($route->getCallback());   // ['app\\controller\\UserController', 'view']
            var_export($route->param());         // ['uid'=>111]
            var_export($route->param('uid'));    // 111 
        }
        return $handler($request);
    }
}

注意
$route->param()方法需要 webman-framework >= 1.3.16

中間件獲取異常

注意
需要 webman-framework >= 1.3.15

業(yè)務(wù)處理過程中可能會產(chǎn)生異常,在中間件里使用 $response->exception() 獲取異常。

路由配置

<?php
use support\Request;
use Webman\Route;

Route::any('/user/{uid}', function (Request $request, $uid) {
    throw new \Exception('exception test');
});

中間件:

<?php
namespace app\middleware;

use Webman\MiddlewareInterface;
use Webman\Http\Response;
use Webman\Http\Request;

class Hello implements MiddlewareInterface
{
    public function process(Request $request, callable $handler) : Response
    {
        $response = $handler($request);
        $exception = $response->exception();
        if ($exception) {
            echo $exception->getMessage();
        }
        return $response;
    }
}

超全局中間件

注意
此特性要求 webman-framework >= 1.5.16

主項目的全局中間件只影響主項目,不會對應(yīng)用插件產(chǎn)生影響。有時候我們想要加一個影響全局包括所有插件的中間件,則可以使用超全局中間件。

config/middleware.php中配置如下:

return [
    '@' => [ // 給主項目及所有插件增加全局中間件
        app\middleware\MiddlewareGlobl::class,
    ], 
    '' => [], // 只給主項目增加全局中間件
];

提示
@超全局中間件不僅可以在主項目配置,也可以在某個插件里配置,例如plugin/ai/config/middleware.php里配置@超全局中間件,則也會影響主項目及所有插件。

給某個插件增加中間件

注意
此特性要求 webman-framework >= 1.5.16

有時候我們想給某個應(yīng)用插件增加一個中間件,又不想改插件的代碼(因為升級會被覆蓋),這時候我們可以在主項目中給它配置中間件。

config/middleware.php中配置如下:

return [
    'plugin.ai' => [], // 給ai插件增加中間件
    'plugin.ai.admin' => [], // 給ai插件的admin模塊(plugin\ai\app\admin目錄)增加中間件
];

提示
當(dāng)然也可以在某個插件中加類似的配置去影響其它插件,例如plugin/foo/config/middleware.php里加入如上配置,則會影響ai插件。

編輯于2025-02-12 23:07:45 完善本頁 +發(fā)起討論
贊助商