آموزش Middleware

اصول آموزش Laravel

تاریخ : پنج شنبه 12 بهمن 1396

middleware مکانیزم مناسبی ارائه می‌دهد که به وسیله آن می‌توان درخواست‌های HTTP را قبل از ورود به برنامه فیلتر کرد. برای مثال، لاراول شامل یک middleware است که بوسیله آن می‌توان مشخص کرد که کاربر برنامه به درستی احراز هویت شده است یا خیر. اگر کاربر تایید نشده باشد، middleware کاربر را دوباره به صفحه ورود به سایت (Login) هدایت می‌کند. ولی اگر کاربر تأیید هویت شده باشد، middleware به درخواست اجازه می‌دهد تا به برنامه وارد شده و عملیات دیگری را نیز انجام دهد. در ادامه بیشتر به middleware در لاراول می‌پردازیم.

البته، middlewareهای دیگری نیز برای انجام کارهای مختلف، علاوه بر احراز هویت نوشته شده است. برای مثال، یک CORS middleware مسئول اضافه کردن سرصفحه‌های مناسب به تمام پاسخ‌هایی است که از برنامه خارج می‌شوند. یک middleware ثبت وقایع، تمام درخواست‌های ورودی به برنامه را ثبت می‌کند.

در فریم ورک لاراول چند middleware به صورت پیش فرض وجود دارند که از جمله آن‌ها می‌توان به middleware احراز هویت و حفاظت CSRF اشاره کرد. همه این middlewareها در دایرکتوری app/Http/Middleware قرار دارند.


تعریف middleware

برای ایجاد یک middleware جدید، از دستور آرتیسان make:middleware استفاده کنید:

php artisan make:middleware CheckAge

این دستور یک کلاس CheckAge جدید را در دایرکتوری app/Http/Middleware ایجاد می‌کند. در این middleware، فقط در صورتی که age بیشتر از 200 باشد، به مسیر دسترسی خواهیم داشت. در غیر این صورت، کاربران را به URI مربوط به home هدایت می‌شوند.

<?php

namespace App\Http\Middleware;

use Closure;

class CheckAge
{
    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     */
    public function handle($request, Closure $next)
    {
        if ($request->age <= 200) {
            return redirect('home');
        }

        return $next($request);
    }
}

همانطور که مشاهده می‌کنید، اگر age داده شده کمتر یا برابر 200 باشد، middleware یک HTTP redirect را به مشتری یا سرویس گیرنده بازمی‌گرداند؛ در غیر این صورت، درخواست به برنامه منتقل خواهد شد. برای ارسال محتاطانه یک درخواست به برنامه (middleware اجازه می‌دهد که درخواست یک pass به برنامه داشته باشد)، به سادگی تابع $next را با آرگومان $request فراخوانی کنید.

می‌توان middleware را به عنوان مجموعه‌ای از لایه‌ها در نظر گرفت که درخواست های HTTP باید از آن‌ها عبور کنند و وارد برنامه شوند. هر لایه باید درخواست‌ها را بررسی کند و حتی می‌تواند آن‌ها را به طور کامل رد کند.


انجام وظایف توسط 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 بصورت Global

اگر می‌خواهید یک middleware بر روی هر درخواست HTTP در برنامه اجرا شود، می‌توانید به سادگی کلاس middleware را در خصوصت $middleware کلاس app/Http/Kernel.php لیست کنید.


اختصاص middleware به مسیر در لاراول

اگر می‌خواهید middleware را به مسیرهای خاصی اختصاص دهید، ابتدا باید یک کلید به middleware در فایل app/Http/Kernel.php اختصاص دهید. در حالت پیش‌فرض، ویژگی $routeMiddleware در این کلاس شامل ثبت middlewareهایی است که در درون لاراول قرار دارند. برای افزودن middleware خود، به سادگی آن را به این لیست اضافه کنید و یک کلید به آن اختصاص دهید. مثال زیر را در نظر بگیرید:

// Within App\Http\Kernel Class...

protected $routeMiddleware = [
    'auth' => \Illuminate\Auth\Middleware\Authenticate::class,
    'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
    'bindings' => \Illuminate\Routing\Middleware\SubstituteBindings::class,
    'can' => \Illuminate\Auth\Middleware\Authorize::class,
    'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
    'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
];

زمانی که middleware در هسته HTTP تعریف شده باشد، می‌توان از متد middleware برای اختصاص middleware به یک مسیر استفاده کرد:

Route::get('admin/profile', function () {
    //
})->middleware('auth');

همچنین می‌توان چند middleware را به مسیر مانند مثال زیر اختصاص داد:

Route::get('/', function () {
    //
})->middleware('first', 'second');

هنگام تعیین middleware، همچنین می‌توانید نام کامل کلاس موردنظر را منتقل کنید:

use App\Http\Middleware\CheckAge;

Route::get('admin/profile', function () {
    //
})->middleware(CheckAge::class);


middleware گروه بندی شده در لاراول

گاهی اوقات ممکن است بخواهید چند middleware را با یک کلید واحد گروه بندی کنید تا به راحتی بتوانید آن‌ها را به مسیرها اختصاص دهید. این کار را با استفاده از خصوصیت $middlewareGroups در هسته HTTP می‌توان انجام داد.

علاوه بر middlewareهای پیش‌فرض لاراول، web و api دو گروه middleware هستند که شامل یک middleware مشترکند که ممکن است بخواهید آن‌ها را به web UI و API routes اعمال کنید:

/**
 * 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های گروه بندی شده به مسیرها و اکشن‌های کنترلر، مثل روش اختصاص middlewareهای تکی است. با استفاده از middlewareهای گروه بندی شده اختصاص چندین middleware به یک مسیر به راحتی انجام می‌گیرد.

Route::get('/', function () {
    //
})->middleware('web');

Route::group(['middleware' => ['web']], function () {
    //
});

گروه middleware web به صورت خودکار توسط RouteServiceProvider به فایل routes/web.php اعمال می‌شود.


middleware پارامترهای

همچنین middleware می‌تواند پارامترهای اضافی نیز دریافت کند. برای مثال، اگر برنامه قبل از انجام هر عملی خاصی از سوی کاربر احراز هویت شده، نیازمند مشخص کردن role مربوط به آن کاربر نیز باشد، بایستی یک middleware CheckRole ایجاد کنید که نام role را به عنوان یک آرگومان دریافت می‌کند.

پارامترهای اضافی بعد از آرگومان $next به middleware منتقل می‌شوند:

<?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 پایان‌ پذیر (Terminable Middleware)

گاهی اوقات بایستی بعضی از کارها توسط یک middleware پس از ارسال پاسخ HTTP به مرورگر، انجام شود. برای مثال، session middleware که به صورت ‌پیش‌فرض در لاراول قرار دارد، اطلاعات session را پس از ارسال پاسخ به مرورگر در حافظه می‌نویسد. اگر متد terminate را در middleware خود تعریف کنید، به صورت خودکار پس از ارسال پاسخ به مرورگر آن را فراخوانی می‌کند.

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

متد terminate باید هم درخواست و هم پاسخ را دریافت کند. زمانی که یک middleware پایان ‌پذیر را تعریف می‌کنید، باید آن را به لیست مسیر یا middlewareهای عمومی در فایل app/Http/Kernel.php اضافه کنید.

هنگام فراخوانی متد terminate در middleware، لاراول یک نمونه از middleware را در service container بایند می‌کند. زمانی که متدهای handle و terminate فراخوانی می‌شوند، اگر بخواهید از یک middleware مشترک استفاده کنید، باید middleware را در container با استفاده از متد singleton ثبت کنید.


منابع مورد مطالعه جهت جمع آوری این مطلب:
https://laravel.com/docs/5.5/middleware
https://www.lydaweb.com/article/courses/laravel-5-5-tutorial/221/کاربرد-middleware-لاراول


نظرات