Middleware
Middleware সাধারণত অনুরোধ বা প্রতিক্রিয়া আটকানোর জন্য ব্যবহৃত হয়। উদাহরণস্বরূপ, কোন নিয়ন্ত্রণকারীর আগে একত্রে ব্যবহারকারীর পরিচয় নিশ্চিত করা, যেমন ব্যবহারকারী লগইন না থাকলে লগইন পৃষ্ঠায় ফিরে যাওয়া, বা প্রতিক্রিয়ায় একটি নির্দিষ্ট header যুক্ত করা। উদাহরণস্বরূপ, একটি নির্দিষ্ট uri অনুরোধের পরিমাণ গণনা করা ইত্যাদি।
Middleware পেঁয়াজ মডেল
┌──────────────────────────────────────────────────────┐
│ middleware1 │
│ ┌──────────────────────────────────────────┐ │
│ │ middleware2 │ │
│ │ ┌──────────────────────────────┐ │ │
│ │ │ middleware3 │ │ │
│ │ │ ┌──────────────────┐ │ │ │
│ │ │ │ │ │ │ │
── Reqeust ───────────────────> Controller ── Response ───────────────────────────> Client
│ │ │ │ │ │ │ │
│ │ │ └──────────────────┘ │ │ │
│ │ │ │ │ │
│ │ └──────────────────────────────┘ │ │
│ │ │ │
│ └──────────────────────────────────────────┘ │
│ │
└──────────────────────────────────────────────────────┘
Middleware এবং নিয়ন্ত্রণকারীরা একটি ক্লাসিক পেঁয়াজ মডেল নিয়ে গঠিত, Middleware একটি স্তরে স্তরে পেঁয়াজের চামড়ার মতো, নিয়ন্ত্রণকারী হল পেঁয়াজের কেন্দ্র। যেমন চিত্রে প্রদর্শিত হয়েছে, অনুরোধগুলি তীরের মতো middleware1, 2, 3 এর মধ্য দিয়ে নিয়ন্ত্রণকারী পর্যন্ত পৌঁছে যায়, নিয়ন্ত্রণকারী একটি প্রতিক্রিয়া ফেরত দেয়, তারপর প্রতিক্রিয়াটি আবার 3, 2, 1 এর পর্যায়ক্রমে মধ্য দিয়ে বেরিয়ে ক্লায়েন্টে ফিরে আসে। অর্থাৎ, প্রতিটি middleware-এ আমরা অনুরোধ পেতে পারি, এবং প্রতিক্রিয়াও পেতে পারি।
Request Intercept
কখনও কখনও আমরা চাই না একটি নির্দিষ্ট অনুরোধ নিয়ন্ত্রণকারী স্তরে পৌঁছে যাক, উদাহরণস্বরূপ, middleware2-এ যখন আমরা দেখতে পাই যে বর্তমান ব্যবহারকারী লগইন করেনি, তাহলে আমরা সরাসরি অনুরোধটি আটকাতে পারি এবং একটি লগইন প্রতিক্রিয়া ফেরত দিতে পারি। তাই এই প্রক্রিয়াটি নিচের মতো।
┌────────────────────────────────────────────────────────────┐
│ middleware1 │
│ ┌────────────────────────────────────────────────┐ │
│ │ middleware2 │ │
│ │ ┌──────────────────────────────┐ │ │
│ │ │ middleware3 │ │ │
│ │ │ ┌──────────────────┐ │ │ │
│ │ │ │ │ │ │ │
── Reqeust ─────────┐ │ │ Controller │ │ │ │
│ │ Response │ │ │ │ │ │
<───────────────────┘ │ └──────────────────┘ │ │ │
│ │ │ │ │ │
│ │ └──────────────────────────────┘ │ │
│ │ │ │
│ └────────────────────────────────────────────────┘ │
│ │
└────────────────────────────────────────────────────────────┘
যেমন চিত্রে দেখানো হয়েছে, যখন অনুরোধ middleware2-এ পৌঁছে যায় তখন একটি লগইন প্রতিক্রিয়া তৈরি হয়, প্রতিক্রিয়া middleware2-এর মধ্য দিয়ে middleware1-এ ফিরে আসে এবং তারপর ক্লায়েন্টে ফেরত পাঠানো হয়।
Middleware Interface
Middleware অবশ্যই 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()
ইত্যাদি সহায়ক ফাংশনের দ্বারা তৈরি প্রতিক্রিয়া হতে পারে (অনুরোধটি পেঁয়াজের কেন্দ্রে অব্যাহত থাকবে না)।
Middleware থেকে Request ও Response পাওয়া
Middleware-এ আমরা অনুরোধ পেতে পারি, এবং নিয়ন্ত্রকটি কার্যকরির পরের প্রতিক্রিয়াও পেতে পারি, তাই Middleware-এর অভ্যন্তরে তিনটি অংশে বিভক্ত করা যেতে পারে।
- অনুরোধকে অতিক্রম করার ধাপ, অর্থাৎ অনুরোধ প্রক্রিয়া করার আগে
- নিয়ন্ত্রক অনুরোধ প্রক্রিয়া করার সময়, অর্থাৎ অনুরোধ প্রক্রিয়া করার ধাপ
- প্রতিক্রিয়া বেরিয়ে আসার ধাপ, অর্থাৎ অনুরোধ প্রক্রিয়া করার পরে
তিনটি ধাপ Middleware-এ নিম্নরূপ প্রকাশ করা হয়েছে।
<?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;
}
}
উদাহরণ: পরিচয় যাচাইকরণ Middleware
নতুন ফাইল তৈরি করুন 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
ফাইলে নিচের মতো গ্লোবাল Middleware যুক্ত করুন:
return [
// গ্লোবাল Middleware
'' => [
// ... এখানে অন্যান্য Middleware বাদ দেওয়া হয়েছে
app\middleware\AuthCheckTest::class,
]
];
পরিচয় যাচাইকরণ Middleware এর মাধ্যমে, আমরা নিয়ন্ত্রক স্তরে ব্যবসায়ের কোড লেখা নিয়ে মনোনিবেশ করতে পারি, ব্যবহারকারীর লগইন অবস্থান সম্পর্কে উদ্বিগ্ন না হয়।
উদাহরণ: ক্রস-অলাভ্য অনুরোধ Middleware
নতুন ফাইল তৈরি করুন 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
{
// যদি এটি options অনুরোধ হয় তবে একটি খালি প্রতিক্রিয়া ফেরত দিন, অন্যথায় পেঁয়াজের কেন্দ্রে অতিক্রম করতে অব্যাহত রাখুন এবং একটি প্রতিক্রিয়া পান
$response = $request->method() == 'OPTIONS' ? response('') : $handler($request);
// প্রতিক্রিয়ার সাথে ক্রস-অলাভ্য সম্পর্কিত http header যোগ করুন
$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;
}
}
হার্সালা
ক্রস-অলাভ্য OPTIONS অনুরোধগুলি তৈরি করতে পারে, আমরা OPTIONS অনুরোধকে নিয়ন্ত্রকে প্রবেশ করতে দিতে চাই না, তাই আমরা OPTIONS অনুরোধের জন্য সরাসরি একটি খালি প্রতিক্রিয়া ফেরত দিয়েছি (response('')
) অনুরোধ আটকানো বাস্তবায়ন করতে।
যদি আপনার API-তে রুট সেটিং করার প্রয়োজন হয়, তবেRoute::any(..)
বাRoute::add(['POST', 'OPTIONS'], ..)
ব্যবহার করুন।
config/middleware.php
ফাইলে নিচের মতো গ্লোবাল Middleware যুক্ত করুন:
return [
// গ্লোবাল Middleware
'' => [
// ... এখানে অন্যান্য Middleware বাদ দেওয়া হয়েছে
app\middleware\AccessControlTest::class,
]
];
দ্রষ্টব্য
যদি AJAX অনুরোধ কাস্টম header তৈরি করে, তবে Middleware-এAccess-Control-Allow-Headers
ক্ষেত্রটিতে এই কাস্টম header যুক্ত করতে হবে, অন্যথায়Request header field XXXX is not allowed by Access-Control-Allow-Headers in preflight response.
ত্রুটি হবে।
ব্যাখ্যা
- Middleware তিন ধরনের হয়: গ্লোবাল Middleware, অ্যাপ্লিকেশন Middleware (অ্যাপ্লিকেশন Middleware শুধুমাত্র একাধিক অ্যাপ্লিকেশন মোডে কার্যকরী হয়, মাল্টি অ্যাপ্লিকেশন দেখুন), রুট Middleware
- Middleware কনফিগারেশন ফাইলের অবস্থান
config/middleware.php
- গ্লোবাল Middleware কনফিগারেশন
''
চাবিতে - অ্যাপ্লিকেশন Middleware নির্দিষ্ট অ্যাপ্লিকেশন ই নামে কনফিগার করা হয়, উদাহরণস্বরূপ
return [
// গ্লোবাল Middleware
'' => [
app\middleware\AuthCheckTest::class,
app\middleware\AccessControlTest::class,
],
// API অ্যাপ্লিকেশন Middleware (অ্যাপ্লিকেশন Middleware শুধুমাত্র একাধিক অ্যাপ্লিকেশন মোডে কার্যকরী হয়)
'api' => [
app\middleware\ApiOnly::class,
]
];
নিয়ন্ত্রক Middleware এবং পদ্ধতি Middleware
আমরা টীকা ব্যবহার করে একটি নিয়ন্ত্রক বা নিয়ন্ত্রকের একটি নির্দিষ্ট পদ্ধতিতে Middleware সেট করতে পারি।
<?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 'হ্যালো';
}
}
রুট Middleware
আমরা একটি রুট বা একটি গ্রুপ রুটে Middleware সেট করতে পারি।
যেমন 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,
]);
Middleware কনস্ট্রাক্টর আর্গুমেন্ট
কনফিগারেশন ফাইলগুলি মিডলওয়্যার বা অ্যানোনিমাস ফাংশন সরাসরি বাস্তবায়িত করতে সমর্থন করে, এইভাবে কনস্ট্রাক্টরের মাধ্যমে মিডলওয়্যারকে আর্গুমেন্ট পাঠানো সহজ হয়।
যেমন config/middleware.php
-এ এভাবে কনফিগার করা যেতে পারে
return [
// গ্লোবাল Middleware
'' => [
new app\middleware\AuthCheckTest($param1, $param2, ...),
function(){
return new app\middleware\AccessControlTest($param1, $param2, ...);
},
],
// API অ্যাপ্লিকেশন Middleware (অ্যাপ্লিকেশন Middleware শুধুমাত্র একাধিক অ্যাপ্লিকেশন মোডে কার্যকরী হয়)
'api' => [
app\middleware\ApiOnly::class,
]
];
একইভাবে রুট Middleware-ও কনস্ট্রাক্টরের মাধ্যমে প্যারামিটার পাস করতে পারে, উদাহরণস্বরূপ 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, ...);
},
]);
Middleware-এ আর্গুমেন্ট ব্যবহারের উদাহরণ
<?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);
}
}
Middleware কার্যকরকরণের ক্রম
- Middleware কার্যকরকরণের ক্রম হল
গ্লোবাল Middleware
->অ্যাপ্লিকেশন Middleware
->নিয়ন্ত্রক Middleware
->রুট Middleware
->পদ্ধতি Middleware
। - যখন একই স্তরে একাধিক Middleware থাকে, এটি একই স্তরের Middleware বাস্তবিক কনফিগারেশন ক্রমের অনুসরণ করবে।
- 404 অনুরোধ ডিফল্টভাবে কোনও Middleware কার্যকর করবেনা (তবে এখনও
Route::fallback(function(){})->middleware()
এর মাধ্যমে Middleware যুক্ত করা যেতে পারে)।
রুট 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']);
Middleware (ধরা যাক গ্লোবাল Middleware)
<?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);
}
}
Middleware থেকে নিয়ন্ত্রকে প্যারামিটার পাঠানো
কখনও কখনও নিয়ন্ত্রক Middleware-এ তৈরি করা ডেটা ব্যবহার করতে পারে, তখন আমরা $request
অবজেক্টে বৈশিষ্ট্য যোগ করে নিয়ন্ত্রকে প্যারামিটার পাঠাতে পারি। উদাহরণস্বরূপ:
Middleware
<?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);
}
}
Middleware থেকে বর্তমান অনুরূপ রুট তথ্য পাওয়া
আমরা $request->route
ব্যবহার করে রুট অবজেক্ট পেতে পারি, এবং সংশ্লিষ্ট তথ্য পাওয়ার জন্য প্রয়োজনীয় পদ্ধতি কল করতে পারি।
রুট কনফিগারেশন
<?php
use support\Request;
use Webman\Route;
Route::any('/user/{uid}', [app\controller\UserController::class, 'view']);
Middleware
<?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);
}
}
দ্রষ্টব্য
Middleware থেকে ব্যতিক্রম পাওয়া
ব্যবসায়িক প্রক্রিয়াগুলির সময় ত্রুটি ঘটতে পারে, Middleware-এ $response->exception()
ব্যবহার করে ব্যতিক্রম পাওয়া যায়।
রুট কনফিগারেশন
<?php
use support\Request;
use Webman\Route;
Route::any('/user/{uid}', function (Request $request, $uid) {
throw new \Exception('exception test');
});
Middleware:
<?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;
}
}
সুপার গ্লোবাল Middleware
প্রধান প্রকল্পের গ্লোবাল Middleware প্রধান প্রকল্পকে প্রভাবিত করে, এটি অ্যাপ্লিকেশন প্লাগিন গুলিতে প্রভাবিত করবে না। কখনও কখনও আমরা একটি গ্লোবাল Middleware যোগ করতে চাই, যা সবকিছুতে প্রভাব ফেলে, সেজন্য আমরা সুপার গ্লোবাল Middleware ব্যবহার করতে পারি।
config/middleware.php
-এ নিচের মতো কনফিগার করুন:
return [
'@' => [ // প্রধান প্রকল্প ও সমস্ত প্লাগিনে গ্লোবাল Middleware যুক্ত করা
app\middleware\MiddlewareGlobl::class,
],
'' => [], // শুধুমাত্র প্রধান প্রকল্পে গ্লোবাল Middleware যুক্ত করা
];
হুঁশিয়ারি
@
সুপার গ্লোবাল Middleware কেবল প্রধান প্রকল্পের কনফিগারেশনেই নয়, বরং কোনও প্লাগিনের ভেতরে কনফিগার করা যেতে পারে, উদাহরণস্বরূপplugin/ai/config/middleware.php
-এ@
সুপার গ্লোবাল Middleware কনফিগার করলে এটি প্রধান প্রকল্প এবং সমস্ত প্লাগিনকেও প্রভাবিত করবে।
কোনও প্লাগিনে Middleware যোগ করা
কখনও কখনও আমরা কোনও অ্যাপ্লিকেশন প্লাগিন-এ Middleware যোগ করতে চাই, কিন্তু প্লাগিনের কোড পরিবর্তন করতে চাই না (কারণ আপডেট হওয়ার সময় তা ওভাররাইট হবে), তখন আমরা মূল প্রকল্পে এটি কনফিগার করতে পারি।
config/middleware.php
-এ নিচের মতো কনফিগার করুন:
return [
'plugin.ai' => [], // ai প্লাগিনে Middleware যোগ করা
'plugin.ai.admin' => [], // ai প্লাগিনের admin মডিউলে Middleware যোগ করা (plugin\ai\app\admin ডিরেক্টরি)
];
হুঁশিয়ারি
অবশ্যই আপনি কোনও প্লাগিনে সাদৃশ্যের মতো কনফিগারেশন যোগ করতে পারেন যাতে অন্যান্য প্লাগিনকে প্রভাবিত করে, উদাহরণস্বরূপplugin/foo/config/middleware.php
-এ উপরের মতো কনফিগারেশন যোগ করলে তা ai প্লাগিনকে প্রভাবিত করবে।