میدلور

مجتبی پاکزاد لاراول 5.6.29 در حال تکمیل رایگان

مقدمه

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

طبیعتا، امکان نوشتن میدلورهای بیشتری وجود دارد تا علاوه بر اعتبارسنجی احراز هویت، بتوان تسک‌های گوناگون دیگری را نیز اجرا کرد. یک میدلور CORS می‌تواند مسئولیت افزودن هدرهای مناسب به تمام ریسپانس‌های تولید شده توسط اپلیکیشن‌تان را به عهده بگیرد. میدلوری با نام logging می‌تواند تمام ریکوئست‌های ورودی به اپلیکیشن‌تان را لاگ کند.

میدلورهای مختلفی در لاراول اینکلود شده‌اند، از جمله میدلورهای authentication و CSRF protection. تمامی این میدلورها، در دایرکتوری app/Http/Middleware قرار گرفته‌اند.

تعریف کردن میدلور

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

php artisan make:middleware CheckAge

این کامند یک کلاس CheckAge جدید در دایرکتوری app/Http/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 باشد، میدلور یک ریدایرکت HTP به کلاینت برمی‌گرداند؛ به عبارت دیگر، ریکوئست اجازه دارد برای پردازش‌های بعدی از سد میدلور عبور کند. برای اینکه ریکوئست به میزان عمیق‌تری به اپلیکیشن وارد شود (میدلور اجازه عبور دهد)، باید کال‌بَک $next با پارامتر $request فراخوانی شود.

بهتر است که میدلور را به عنوان یک سری "لایه" برای ریکوئست‌های HTTP در نظر بگیریم که قبل از رسیدن به اپلیکیشن، باید از آن‌ها عبور کنند. هر لایه می‌تواند ریکوئست را مورد بررسی قرار داده و یا حتی به صورت کامل آن را رد کند.

تمامی میدلورها توسط سرویس کانتینرها ریزالو می‌شوند، بنابراین می‌تواند هر وابستگی را که می‌خواهید درون متد constructor میدلور type-hint کنید.

میدلورهای Before و After

اینکه یک میدلور، قبل یا بعد از یک ریکوئست اجرا شود، به خود میدلور بستگی دارد. برای مثال، میدلور زیر می‌تواند به منظور اجرای تعدادی تسک، قبل از اینکه ریکوئست توسط اپلیکیشن هندل شود، استفاده شود:

<?php

namespace App\Http\Middleware;

use Closure;

class BeforeMiddleware
{
    public function handle($request, Closure $next)
    {
        // Perform action

        return $next($request);
    }
}

اما، این میدلور می‌تواند به منظور اجرای تسک‌های خود، بعد از اینکه ریکوئست توسط اپلیکیشن هندل شد، استفاده شود:

<?php

namespace App\Http\Middleware;

use Closure;

class AfterMiddleware
{
    public function handle($request, Closure $next)
    {
        $response = $next($request);

        // Perform action

        return $response;
    }
}

رجیستر کردن میدلورها

گلوبال میدلور

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

تعیین میدلور برای روت

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

// 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,
];

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

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

همچنین می‌توانید برای یک روت چندین میدلور تعیین کنید:

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

همچنین در هنگام تعیین میدلور، می‌توانید نام کامل کلاس آن را به متد middleware پاس دهید:

use App\Http\Middleware\CheckAge;

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

گروه‌های میدلور

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

به صورت از قبل آماده شده، گروه میدلورهای web و api در لاراول گنجانده شده‌اند که حاوی میدلور مشترکی هستند که می‌توانید به روت‌های API و web اعمال کنید:

/**
 * 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',
    ],
];

با استفاده از سینتکسی مشابه سینتکس میدلور، می‌توان گروه‌های میدلور را نیز به روت‌ها یا اکشن‌های کنترلر تخصیص داد. یادآوری، گروه‌های میدلوری، تخصیص یک‌باره تعدادی میدلور به یک روت را راحت‌تر می‌کنند:

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

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

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

پارامترهای میدلور

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

پارامترهای اضافه میدلور، بعد از آرگومان $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);
    }

}

پارامترهای میدلور می‌توانند در هنگام تعریف روت مشخص شوند، نام میدلور و پارامترا با : جدا می‌شوند. پارامترهای بعدی نیز با کاما جدا می‌شوند:

Route::put('post/{id}', function ($id) {
    //
})->middleware('role:editor');

میدلور پایان‌پذیر

گاهی یک میدلور ممکن است نیاز به انجام کارهایی پس از آماده شدن ریسپانس HTTP داشته باشد. برای مثال، میدلور session که در لاراول اینکلود شده است، پس از اینکه ریسپانس کاملا آماده شد، داده‌های سشن را در storage می‌نویسد. اگر یک متد 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...
    }
}

متد terminate باید هم ریکوئست و هم ریسپانس را دریافت کند. هنگامی که یک میدلور terminable تعریف می‌کنید، باید آن را به لیست میدلورهای روت یا میدلورهای گلوبال در فایل app/Http/Kernel.php اضافه کنید.

هنگامی که متد terminate بر روی میدلور فراخوانی می‌شود، لاراول یک نمونه تازه (fresh instance) از میدلور را از داخل سرویس کانتینر ریزالو می‌کند. اگر می‌خواهید هنگامی که متدهای و فراخوانی می‌شوند، از نمونه میدلور یکسانی استفاده کنید، با استفاده از متد singleton کانتینر، آن میدلور را در کانتینر رجیستر کنید.

منبع
Middleware