मिडलवेयर

मिडलवेयर सामान्यतः अनुरोध या प्रतिक्रिया को अवरोधित करने के लिए उपयोग किया जाता है। उदाहरण के लिए, नियंत्रक को कॉल करने से पहले उपयोगकर्ता की पहचान को सत्यापित करना, जैसे कि यदि उपयोगकर्ता लॉगिन नहीं है तो लॉगिन पृष्ठ पर पुनर्निर्देशित करना, या प्रतिक्रिया में किसी हेडर को जोड़ना। उदाहरण के लिए, किसी विशिष्ट URI अनुरोध का प्रतिशत सांख्यिकी बनाना आदि।

मिडलवेयर प्याज मॉडल


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

मिडलवेयर और नियंत्रक एक क्लासिक प्याज मॉडल बनाते हैं, जिसमें मिडलवेयर एक परत की तरह होते हैं और नियंत्रक प्याज का केंद्र होता है। जैसा कि चित्र में दिखाया गया है, अनुरोध तीर की तरह मिडलवेयर 1, 2, 3 के माध्यम से नियंत्रक तक पहुँचता है, जहाँ नियंत्रक एक प्रतिक्रिया लौटाता है, और फिर प्रतिक्रिया मिडलवेयर 3, 2, 1 के क्रम में बाहर निकलती है और अंततः क्लाइंट को लौटाई जाती है। इसका मतलब है कि प्रत्येक मिडलवेयर में हम अनुरोध को भी प्राप्त कर सकते हैं और प्रतिक्रिया को भी प्राप्त कर सकते हैं।

अनुरोध अवरोधन

कभी-कभी हम नहीं चाहते कि कोई विशेष अनुरोध नियंत्रक स्तर तक पहुंचे। उदाहरण के लिए, यदि हम middleware2 में पाते हैं कि वर्तमान उपयोगकर्ता लॉगिन नहीं है, तो हम सीधे अनुरोध को अवरोधित कर सकते हैं और एक लॉगिन प्रतिक्रिया लौटा सकते हैं। तो यह प्रक्रिया इस तरह दिखाई देगी:


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

जैसा कि चित्र में दिखाया गया है, अनुरोध middleware2 तक पहुँचता है और एक लॉगिन प्रतिक्रिया उत्पन्न करता है, प्रतिक्रिया middleware2 के माध्यम से middleware1 में वापस आती है और फिर क्लाइंट को लौटाई जाती है।

मिडलवेयर इंटरफेस

मिडलवेयर को 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;
}

इसका मतलब है कि इसे process विधि को लागू करना होगा, process विधि को एक support\Response वस्तु लौटानी होगी, और डिफ़ॉल्ट रूप से यह वस्तु $handler($request) द्वारा उत्पन्न की जाती है (अनुरोध प्याज केंद्र से आगे बढ़ता रहता है), या यह प्रतिक्रिया (response()) json() xml() redirect() जैसे सहायक कार्यों द्वारा उत्पन्न की जा सकती है (अनुरोध प्याज केंद्र से आगे बढ़ना बंद कर देता है)।

मिडलवेयर में अनुरोध और प्रतिक्रिया प्राप्त करना

मिडलवेयर में हम अनुरोध और नियंत्रक के बाद प्रतिक्रिया प्राप्त कर सकते हैं, इसलिए मिडलवेयर को तीन भागों में विभाजित किया जा सकता है।

  1. अनुरोध पारित चरण, यानी अनुरोध प्रक्रिया से पहले का चरण
  2. नियंत्रक अनुरोध प्रक्रिया चरण, यानी अनुरोध की प्रक्रिया का चरण
  3. प्रतिक्रिया बाहर निकलने का चरण, यानी अनुरोध प्रक्रिया के बाद का चरण

तीन चरणों का मिडलवेयर में रूपांतरण इस प्रकार है:

<?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); // प्याज केंद्र से आगे बढ़ता है, जब तक नियंत्रक द्वारा प्रतिक्रिया नहीं मिलती

        echo 'यह प्रतिक्रिया बाहर निकलने का चरण है, यानी अनुरोध प्रक्रिया के बाद';

        return $response;
    }
}

उदाहरण: प्रमाणीकरण मिडलवेयर

फाइल app/middleware/AuthCheckTest.php बनाएं (यदि डाइरेक्टरी मौजूद नहीं है, तो कृपया स्वयं बनाएं) इस प्रकार:

<?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')) {
            // पहले से लॉगिन है, अनुरोध प्याज केंद्र से आगे बढ़ता है
            return $handler($request);
        }

        // रिफ्लेक्शन के माध्यम से पता लगाएं कि नियंत्रक की कौन सी विधियाँ लॉगिन की आवश्यकता नहीं हैं
        $controller = new ReflectionClass($request->controller);
        $noNeedLogin = $controller->getDefaultProperties()['noNeedLogin'] ?? [];

        // जो विधि आप एक्सेस करना चाहते हैं उसे लॉगिन की आवश्यकता है
        if (!in_array($request->action, $noNeedLogin)) {
            // अनुरोध को अवरोधित करें, एक पुनर्निर्देशन प्रतिक्रिया लौटाएं, अनुरोध प्याज केंद्र से आगे नहीं बढ़ता है
            return redirect('/user/login');
        }

        // लॉगिन की आवश्यकता नहीं है, अनुरोध प्याज केंद्र से आगे बढ़ता है
        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' => 'लॉगिन ठीक है']);
    }

    public function info()
    {
        return json(['code' => 0, 'msg' => 'ठीक है', 'data' => session('user')]);
    }
}

ध्यान दें
$noNeedLogin में उस नियंत्रक की विधियाँ सूचीबद्ध हैं जिन्हें लॉगिन की आवश्यकता नहीं है।

config/middleware.php में वैश्विक मिडलवेयर जोड़ें इस प्रकार:

return [
    // वैश्विक मिडलवेयर
    '' => [
        // ... यहां अन्य मिडलवेयर छोड़ दिए गए हैं
        app\middleware\AuthCheckTest::class,
    ]
];

प्रमाणीकरण मिडलवेयर के साथ, हम नियंत्रक स्तर पर व्यापार कोड लिखने पर ध्यान केंद्रित कर सकते हैं, बिना यह चिंता किए कि क्या उपयोगकर्ता लॉगिन है।

उदाहरण: क्रॉस-ओरिजिन अनुरोध मिडलवेयर

फाइल app/middleware/AccessControlTest.php बनाएं (यदि डाइरेक्टरी मौजूद नहीं है, तो कृपया स्वयं बनाएं) इस प्रकार:

<?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
    {
        // यदि विकल्प अनुरोध है, तो एक खाली प्रतिक्रिया लौटाएं, अन्यथा प्याज केंद्र से आगे बढ़ें और एक प्रतिक्रिया प्राप्त करें
        $response = $request->method() == 'OPTIONS' ? response('') : $handler($request);

        // प्रतिक्रिया में क्रॉस-ओरिजिन से संबंधित 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;
    }
}

संकेत
क्रॉस-ओरिजिन विकल्प अनुरोध उत्पन्न कर सकता है, हम नहीं चाहते कि विकल्प अनुरोध नियंत्रक तक पहुंचे, इसलिए हम विकल्प अनुरोध के लिए सीधे एक खाली प्रतिक्रिया लौटाते हैं (response('')) जिससे अनुरोध को अवरोधित किया जा सके।
यदि आपके इंटरफ़ेस को रूट सेट करने की आवश्यकता है, तो कृपया Route::any(..) या Route::add(['POST', 'OPTIONS'], ..) का उपयोग करें।

config/middleware.php में वैश्विक मिडलवेयर जोड़ें इस प्रकार:

return [
    // वैश्विक मिडलवेयर
    '' => [
        // ... यहां अन्य मिडलवेयर छोड़ दिए गए हैं
        app\middleware\AccessControlTest::class,
    ]
];

ध्यान दें
यदि एजेएक्स अनुरोध ने हेडर को कस्टमाइज़ किया है, तो मिडलवेयर में Access-Control-Allow-Headers क्षेत्र में इस कस्टम हेडर को जोड़ना आवश्यक है, अन्यथा Request header field XXXX is not allowed by Access-Control-Allow-Headers in preflight response. त्रुटि उत्पन्न होगी।

विवरण

  • मिडलवेयर को वैश्विक मिडलवेयर, एप्लिकेशन मिडलवेयर (एप्लिकेशन मिडलवेयर केवल बहु-एप्लिकेशन मोड में प्रभावी होते हैं, बहु-एप्लिकेशन देखें) और रूट मिडलवेयर में विभाजित किया गया है।
  • मिडलवेयर कॉन्फ़िगरेशन फ़ाइल का स्थान config/middleware.php में है।
  • वैश्विक मिडलवेयर '' कुंजी के अंतर्गत कॉन्फ़िगर किया गया है।
  • एप्लिकेशन मिडलवेयर को विशेष एप्लिकेशन के नाम के अंतर्गत कॉन्फ़िगर किया गया है, उदाहरण के लिए:
return [
    // वैश्विक मिडलवेयर
    '' => [
        app\middleware\AuthCheckTest::class,
        app\middleware\AccessControlTest::class,
    ],
    // एपीआई एप्लिकेशन मिडलवेयर (एप्लिकेशन मिडलवेयर केवल बहु-एप्लिकेशन मोड में प्रभावी होते हैं)
    'api' => [
        app\middleware\ApiOnly::class,
    ]
];

नियंत्रक मिडलवेयर और विधि मिडलवेयर

एनोटेशन का उपयोग करके, हम किसी विशेष नियंत्रक या नियंत्रक की किसी विशेष विधि के लिए मिडलवेयर सेट कर सकते हैं।

<?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 'नमस्ते';
    }
}

रूट मिडलवेयर

हम किसी विशेष रूट या रूट के एक समूह के लिए मिडलवेयर सेट कर सकते हैं।
उदाहरण के लिए, 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,
]);

मिडलवेयर कंस्ट्रक्टर पैरामीटर

कॉन्फ़िगरेशन फ़ाइलों को मिडलवेयर या शब्दहीन फ़ंक्शन को सीधे इंस्टेंटियेट करने का समर्थन करता है, जिससे मिडलवेयर को कंस्ट्रक्टर के माध्यम से पैरामीटर पास करना आसान हो जाता है।
उदाहरण के लिए, config/middleware.php में आप इस प्रकार कॉन्फ़िगर कर सकते हैं:

return [
    // वैश्विक मिडलवेयर
    '' => [
        new app\middleware\AuthCheckTest($param1, $param2, ...),
        function(){
            return new app\middleware\AccessControlTest($param1, $param2, ...);
        },
    ],
    // एपीआई एप्लिकेशन मिडलवेयर (एप्लिकेशन मिडलवेयर केवल बहु-एप्लिकेशन मोड में प्रभावी होते हैं)
    'api' => [
        app\middleware\ApiOnly::class,
    ]
];

इसी प्रकार, रूट मिडलवेयर भी कंस्ट्रक्टर के माध्यम से पैरामीटर भेज सकता है, जैसे कि 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, ...);
    },
]);

मिडलवेयर के भीतर पैरामीटर का उपयोग करने का उदाहरण:

<?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);
    }
}

मिडलवेयर निष्पादन क्रम

  • मिडलवेयर निष्पादन क्रम वैश्विक मिडलवेयर->एप्लिकेशन मिडलवेयर->नियंत्रक मिडलवेयर->रूट मिडलवेयर->विधि मिडलवेयर है।
  • जब एक ही स्तर पर एक से अधिक मिडलवेयर होते हैं, तो वे उसी स्तर पर वास्तविक कॉन्फ़िगरेशन क्रम के अनुसार निष्पादित होते हैं।
  • 404 अनुरोध सामान्यत: कोई मिडलवेयर सक्रिय नहीं करता है (हालांकि, आप Route::fallback(function(){})->middleware() का उपयोग करके मिडलवेयर जोड़ सकते हैं)।

रूट से मिडलवेयर को पैरामीटर भेजना (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']);

मिडलवेयर (मान लीजिए कि यह वैश्विक मिडलवेयर है)

<?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);
    }
}

मिडलवेयर से नियंत्रक को पैरामीटर भेजना

कभी-कभी नियंत्रक को मिडलवेयर द्वारा उत्पन्न डेटा की आवश्यकता होती है, इस प्रकार हम $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 = 'कुछ मान';
        return $handler($request);
    }
}

नियंत्रक:

<?php
namespace app\controller;

use support\Request;

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

मिडलवेयर से वर्तमान अनुरोध रूट जानकारी प्राप्त करना

हम $request->route का उपयोग करके रूट ऑब्जेक्ट प्राप्त कर सकते हैं और संबंधित सूचना प्राप्त करने के लिए उपयुक्त विधियों को कॉल कर सकते हैं।

रूट कॉन्फ़िगरेशन

<?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 होगा
        // मान लें कि ब्राउज़र का एड्रेस /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);
    }
}

ध्यान दें

मिडलवेयर से अपवाद प्राप्त करना

व्यापार प्रक्रिया के दौरान अपवाद उत्पन्न हो सकते हैं, मिडलवेयर में $response->exception() का उपयोग करके अपवाद प्राप्त किया जा सकता है।

रूट कॉन्फ़िगरेशन

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

Route::any('/user/{uid}', function (Request $request, $uid) {
    throw new \Exception('अपवाद परीक्षण');
});

मिडलवेयर:

<?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;
    }
}

सुपर ग्लोबल मिडलवेयर

मुख्य प्रोजेक्ट का वैश्विक मिडलवेयर केवल मुख्य प्रोजेक्ट को प्रभावित करता है, ऐप्लिकेशन प्लगइन्स को प्रभावित नहीं करेगा। कभी-कभी हम सभी प्लगइन्स सहित एक वैश्विक प्रभाव डालने वाला मिडलवेयर जोड़ना चाहते हैं, तो हम सुपर ग्लोबल मिडलवेयर का उपयोग कर सकते हैं।

config/middleware.php में निम्नलिखित कॉन्फ़िगरेशन करें:

return [
    '@' => [ // मुख्य प्रोजेक्ट और सभी प्लगइन्स पर वैश्विक मिडलवेयर जोड़ें
        app\middleware\MiddlewareGlobl::class,
    ], 
    '' => [], // केवल मुख्य प्रोजेक्ट पर वैश्विक मिडलवेयर जोड़ें
];

संकेत
@ सुपर ग्लोबल मिडलवेयर केवल मुख्य प्रोजेक्ट में कॉन्फ़िगर नहीं की जा सकती, बल्कि किसी विशिष्ट प्लगइन में भी कॉन्फ़िगर की जा सकती है, उदाहरण के लिए plugin/ai/config/middleware.php में @ सुपर ग्लोबल मिडलवेयर जोड़ें, तो यह भी मुख्य प्रोजेक्ट और सभी प्लगइन्स को प्रभावित करेगा।

किसी विशेष प्लगइन में मिडलवेयर जोड़ना

कभी-कभी हम किसी विशेष ऐप्लिकेशन प्लगइन में मिडलवेयर जोड़ना चाहते हैं, लेकिन प्लगइन के कोड को संशोधित नहीं करना चाहते (क्योंकि अपडेट के दौरान यह अधिलेखित हो जाएगा), इस स्थिति में हम मुख्य प्रोजेक्ट में उसका मिडलवेयर कॉन्फ़िगर कर सकते हैं।

config/middleware.php में निम्नलिखित कॉन्फ़िगरेशन करें:

return [
    'plugin.ai' => [], // ai प्लगइन में मिडलवेयर जोड़ें
    'plugin.ai.admin' => [], // ai प्लगइन के admin मॉड्यूल (plugin\ai\app\admin डाइरेक्टरी) में मिडलवेयर जोड़ें
];

संकेत
निश्चित रूप से किसी प्लगइन में इसी तरह की कॉन्फ़िगरेशन जोड़कर अन्य प्लगइन्स को प्रभावित किया जा सकता है, उदाहरण के लिए plugin/foo/config/middleware.php में उपरोक्त कॉन्फ़िगरेशन जोड़ें, तो यह ai प्लगइन को भी प्रभावित करेगा।