مبحث Middleware در Laravel

Middleware in Laravelچرا Middleware؟تصور کنید یک سیستم برای ثبت دارایی های افراد پیاده سازی کرده اید. به طوری که فقط مدیران سیستم اجازه ثبت دارایی برای کاربران را دارند (خود کاربران نباید اجازه ثبت هیچ دارایی برای خود داشته باشند). برای این کار شما ابتدا فرمی را در پنل مدیر ایجاد می کنید و با استفاده از آن فرم برای کاربر دارایی ثبت میکنید. این فرم با استفاده از متد post و url ای که در پوشه route تعریف کرده اید عمل میکند. به طوری که url مربوط به آن فرم، متد مربوط به آن url را از controller اجرا میکند. که این متد مربوط به ذخیره دارایی در پایگاه داده مربوط به کاربر مورد نظر است. حال اگر کاربر url مربوط به صفحه ثبت دارایی را بداند، بدون این که مدیر سیستم باشد، میتواند برای خود یا دیگر کاربران، دارایی ثبت کند! که این مشکل بزرگی برای امنیت سیستم پیاده سازی شده است! این یکی از مشکلاتی است که در زمان استفاده از درخواست های HTTP به وجود می آید. برای حل این مشکل، لاراول مکانیزم Middleware را ارائه کرده است. تعریف Middleware(میان افزار)یک مکانیزم مناسب برای فیلتر کردن درخواستهای HTTP که وارد برنامه شما می شوند، است. به عنوان مثال ، Laravel شامل middleware ای است که تأیید اعتبار کاربر برنامه شما را تأیید می کند. در صورت عدم احراز هویت کاربر ، middleware، کاربر را به صفحه ورود به سیستم هدایت می کند. اما ، در صورت احراز هویت کاربر ، middleware به شما اجازه می دهد تا بیشتر به برنامه وارد شوید.میان افزارهای اضافی را می توان برای انجام انواع کارها علاوه بر تأیید اعتبار کاربر ، نوشت. چندین middleware در چارچوب Laravel وجود دارد ، از جمله middleware برای احراز هویت و محافظت از CSRF. همه این middlewareها در مسیر /app/Http/Middleware قرار دارند.ساخت Middleware جدیداگر با لاراول آشنا باشید قطعا با دستورات php artisan آشنایی دارید!برای ساخت یک middleware جدید از دستورphp artisan make : middlewareاستفاده میکنیم. middleware ایجاد شده در مسیر /app/Http/Middleware قرار دارد.php artisan help make:middlewareبا توجه به راهنمایی تصویر بالا میتوان از این دستور استفاده کرد. برای مثال برای ساخت یک middleware برای کنترل نقش مدیر از دستور زیر استفاده میکنیم.php artisan make : middleware IsAdminاین دستور یک کلاس IsAdmin جدید را در پوشه app/Http/Middleware شما قرار می دهد. در این middleware ، تنها در صورتی که نقش کاربر، مدیر باشد ، اجازه دسترسی به درخواست های بعدی را خواهیم داد. در غیر این صورت ، کاربران را به url پنل کاربر(user/) هدایت می کنیم. <?php namespace App\Http\Middleware; use Closure; class IsAdmin{ /** * Handle an incoming request. * * @param \Illuminate\Http\Request $request * @param \Closure $next * @return mixed */ public function handle($request, Closure $next){ if (!$request->isAdmin) { return redirect('user'); } return $next($request); } }همان طور که در کد بالا مشاهده میکنید، اگر کاربر، admin نباشد، به مسیر user/ هدایت می شود. در غیر این صورت درخواست بیشتر به برنامه ارسال میشود. بعد از تایید شدن middleware درخواست بعدی callback می شود.بهتر است قبل از وارد شدن به برنامه ، از middleware به عنوان مجموعه ای از درخواست های HTTP استفاده کنید. هر لایه می تواند درخواست را بررسی کند و حتی آن را به طور کامل رد کند.همه middleware ها از طریق service container حل می شوند ، بنابراین شما می توانید وابستگی های مورد نیاز خود را در یک سازنده middleware تایپ کنید.قبل و بعد از Middlewareاین که آیا یک middleware پیش از درخواست یا بعد از درخواست اجرا شود ، به خود middleware بستگی دارد. به عنوان مثال ، middleware زیر قبل از درخواست توسط برنامه ، وظایفی را انجام می دهد:<?php namespace App\Http\Middleware; use Closure; class BeforeMiddleware{ public function handle($request, Closure $next){ // Perform action return $next($request); } }در حالی که ، این middleware بعد از اینکه درخواست توسط برنامه درخواست شد ، وظیفه خود را انجام می دهد:<?php namespace App\Http\Middleware; use Closure; class AfterMiddleware{ public function handle($request, Closure $next){ $response = $next($request); // Perform action return $response; } }ثبت Middlewareمیان افزار سراسریاگر می خواهید یک middleware در طی هر درخواست HTTP به برنامه شما اجرا شود ، کلاس middleware را در ویژگی middleware$ کلاس app/Http/Kernel.php وارد کنید.اختصاص Middleware به مسیرهااگر میخواهید middleware را به مسیرهای خاص اختصاص دهید ، ابتدا باید middleware را یک کلید در فایل app/Http/Kernel.php خود اختصاص دهید. به طور پیش فرض ، ویژگی routeMiddleware$ این کلاس شامل ورودی هایی برای middleware همراه با Laravel است. برای اضافه کردن موارد دلخواه خود ، آن را در این لیست قرار دهید و کلید انتخابی خود را به آن اختصاص دهید:// Within App\Http\Kernel Class... protected $routeMiddleware = [ 'auth' => \App\Http\Middleware\Authenticate::class, 'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class, 'bindings' => \Illuminate\Routing\Middleware\SubstituteBindings::class, 'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class, 'can' => \Illuminate\Auth\Middleware\Authorize::class, 'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class, 'signed' => \Illuminate\Routing\Middleware\ValidateSignature::class, 'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class, 'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class, ];پس از تعریف middleware در HTTP kernel ، شما می توانید از متد middleware برای اختصاص middleware به یک مسیر استفاده کنید:Route::get('user/profile', function () { // })->middleware('auth');همچنین میتوانید چندین middleware را به مسیر اختصاص دهید:Route::get('/', function () { // })->middleware('first', 'second');در هنگام اختصاص middleware ، شما همچنین می توانید نام کامل کلاس واجد شرایط را وارد کنید:use App\Http\Middleware\IsAdmin; Route::get('admin/profile', function () { // })->middleware(IsAdmin::class);هنگام اختصاص middleware به گروهی از مسیرها ، گاهی ممکن است لازم باشد كه از كاربرد middleware برای یك مسیر اختصاصی در گروه جلوگیری كنید. شما می توانید این روش را با استفاده از روش withoutMiddleware انجام دهید:use App\Http\Middleware\IsAdmin; Route::middleware([IsAdmin::class])->group(function () { Route::get('/admin', function () { // }); Route::get('/', function () { // })->withoutMiddleware([IsAdmin::class]); });متد withoutMiddlewareفقط می تواند middleware مسیر را حذف کند و برای middleware سراسری کاربرد ندارد.گروه های Middlewareبعضی اوقات ممکن است بخواهید چندین middleware را با یک کلید واحد گروه بندی کنید تا آنها را آسان تر در مسیرها اختصاص دهید. شما میتوانید این کار را با استفاده از خاصیت middlewareGroups$ در kernel HTTP خود انجام دهید.لاراول دارای گروه های میان افزار WEB و API است که حاوی middleware رایج است که ممکن است بخواهید در مسیرهای web UI و API خود اعمال کنید:/** * The application's route middleware groups. * * @var array */ protected $middlewareGroups = [ 'web' => [ \App\Http\Middleware\EncryptCookies::class, \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class, \Illuminate\Session\Middleware\StartSession::class, \Illuminate\View\Middleware\ShareErrorsFromSession::class, \App\Http\Middleware\VerifyCsrfToken::class, \Illuminate\Routing\Middleware\SubstituteBindings::class, ], 'api' => [ 'throttle:60,1', 'auth:api', ], ];گروه های Middleware ممکن است به مسیرها و اقدامات کنترل کننده(controller) با استفاده از همان نحوی که middleware های فردی اختصاص داده شده باشد ، اختصاص یابد. مجدداً ، اختصاص دادن گروه های middleware بسیار راحت تر از اختصاص تعداد زیادی middleware به یک مسیر است:Route::get('/', function () { // })->middleware('web'); Route::group(['middleware' => ['web']], function () { // }); Route::middleware(['web', 'subscribed'])->group(function () { // });گروه middleware وب به طور خودکار توسط RouteServiceProvider به پرونده routes/web.php شما اعمال می شود.مرتب سازی Middlewareبه ندرت ، ممکن است شما برای اجرای middleware یک ترتیب خاص نیاز داشته باشید اما هنگام تعیین تکلیف در مسیر ، نظم آنها را کنترل نکنید. در این حالت ، شما می توانید اولویت میان افزار خود را با استفاده از ویژگی middlewarePriority$ در فایل app/Http/Kernel.php برنامه خود مشخص کنید:/** * The priority-sorted list of middleware. * * This forces non-global middleware to always be in the given order. * * @var array */ protected $middlewarePriority = [ \Illuminate\Session\Middleware\StartSession::class, \Illuminate\View\Middleware\ShareErrorsFromSession::class, \Illuminate\Contracts\Auth\Middleware\AuthenticatesRequests::class, \Illuminate\Routing\Middleware\ThrottleRequests::class, \Illuminate\Session\Middleware\AuthenticateSession::class, \Illuminate\Routing\Middleware\SubstituteBindings::class, \Illuminate\Auth\Middleware\Authorize::class, ];پارامترهای Middlewareمیان افزار همچنین می تواند پارامترهای دیگری را دریافت کند. به عنوان مثال ، اگر برنامه شما قبل از انجام یک عمل خاص باید تأیید کند که کاربر تأیید شده دارای "نقش" خاصی است ، می توانید یک واسطه CheckRole ایجاد کنید که یک نام نقش را به عنوان یک آرگومان اضافی دریافت می کند.پارامترهای اضافی middleware بعد از آرگمان next$ به میان افزار منتقل می شود:<?php namespace App\Http\Middleware; use Closure; class CheckRole{ /** * Handle the incoming request. * * @param \Illuminate\Http\Request $request * @param \Closure $next * @param string $role * @return mixed */ public function handle($request, Closure $next, $role){ if (! $request->user()->hasRole($role)) { // Redirect... } return $next($request); } }پارامترهای Middleware ممکن است هنگام تعیین مسیر با جدا کردن نام Middleware و پارامترها با یک ':' مشخص شوند. چندین پارامتر باید با کاما مشخص شود:Route::put('post/{id}', function ($id) { // })->middleware('role:editor');میان افزار (Middleware) خاتمه پذیربعضی اوقات ممکن است middleware بعد از ارسال پاسخ HTTP به مرورگر ، برخی کارها را انجام دهد. اگر یک روش خاتمه را در میان افزار خود تعریف کنید و سرور وب شما از FastCGI استفاده می کند ، پس از ارسال پاسخ به مرورگر ، روش terminate به طور خودکار فراخوانی می شود:<?php namespace Illuminate\Session\Middleware; use Closure; class StartSession{ public function handle($request, Closure $next){ return $next($request); } public function terminate($request, $response){ // Store the session data... } }متد خاتمه باید هم درخواست و هم پاسخ را دریافت کند. پس از تعریف middleware خاتمه پذیر ، باید آن را به لیست route یا middleware سراسری در فایل app/Http/Kernel.php اضافه کنید.هنگام فراخوانی متد terminate در middleware ، لاراول نمونه جدیدی از middleware را از service container رفع می کند. اگر دوست دارید از همان نمونه میان افزار استفاده کنید که متد های handle و terminate فراخوانی شد ، middleware را با استفاده از متد Singleton در container ثبت کنید. معمولاً این کار باید به متد register در AppServiceProvider.php انجام می شود:use App\Http\Middleware\TerminableMiddleware; /** * Register any application services. * * @return void */ public function register(){ $this->app->singleton(TerminableMiddleware::class); }مراجع: laravel.com