فصل ۸: Response Facade و مدیریت پاسخ در لاراول

06 آبان 1397
درسنامه درس 9 از سری لاراول
Laravel-Main-response

در مقاله‌ی فصل ۷ به آموزش و یادگیری نحوه‌ی ارسال درخواست به سرور و همچنین مدیریت داده‌ها پرداختیم. اگر بخواهیم قانون سوم نیوتن (هر نیرویی به جسمی وارد شود دقیقا یک نیرو در خلاف جهت آن وارد می‌شود) را به برنامه‌نویسی ربط دهیم اینگونه می‌توان بیان که هر درخواستی از طرف کاربر پاسخی از طرف سرور خواهد داشت. حال ما در فصل گذشته به توضیح درخواست پرداختیم ولی پاسخ را بررسی نکردیم. از طرفی یکی از مباحث مهمی که در اپلیکیشن‌های تحت وب بیان می‌شود و حائز اهمیت می‌باشد بحث Redirect می‌باشد که خود نیز نوعی پاسخ (Response) می‌باشد و آن را نیز در این بخش توضیح خواهیم داد.

Response Facade

تمام routeها و کنترلرها باید یک پاسخ به ازای هر درخواست به مرورگر کاربر ارسال کنند. لاراول راه‌های متفاوتی برای ارسال پاسخ‌ها درنظر گرفته است. همانطور که قبلا و در دروس اولیه مشاهده کردید ساده‌ترین نوع پاسخ ارسال رشته به صفحه‌ی کاربر می‌باشد. که به صورت زیر می‌باشد:

Route::get('/', function () {
    return 'Hello World';
});

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

Route::get('/', function () {
    return [1, 2, 3];
});

بنابراین می‌توان پاسخ‌های دلخواه به خروجی‌ها ارسال کرد.

ارسال داده‌ها به خروجی

متدهای موجود در یک کنترلر گاها پردازش‌هایی روی اطلاعاتی که از درخواست‌ها بدست می‌آورند انجام می‌دهد و سپس آنها را درون یک متغییر با نام‌های متفاوت ذخیره می‌کنند. قطعا نیاز هست که این داده‌های پردازش ‌شده را در قالبی خاص به خروجی و view ارسال کرده تا در ظاهری مناسب به کاربر نمایش داده شود. برای ارسال داده‌های موجود در متغییر می‌توان از پارامتر دوم دستور View::make یا ()view‌ استفاده کرد. که این شیوه‌ی ارسال معمولا دو صورت است:

ارسال آرایه‌ای از داده‌ها

می‌توان به عنوان آرگومان دوم مجموعه‌ای از رشته‌ها را در قالب یک آرایه ارسال کرد:

Route::get('home', function () {
    $names = ['ali','masoud','roxo.ir'];
    return view('home', $names);
});

با اجرای این دستور رشته‌های موجود در آرایه‌ی names به view موردنظر ارسال می‌شوند.

compact

به عنوان یکی از توابع پرکاربر PHP در لاراول می‌باشد که وظیفه‌ی آن متصل کردن تمام رشته‌ها و ارسال آنها تحت عنوان یک متغییر واحد به خروجی view است:

Route::get('home', function () {
    $names = ['ali','masoud','roxo.ir'];
    $newNames = ['ask.roxo.ir','nemo'];
    $allArray = compact(['names','newNames']);
    return view('home', $allArray);
});

در این حالت هر دو رشته‌ی names و newNames داخل متغییر allArray ذخیره شده و به خروجی home ارسال می‌شوند.

اشیاء Response

در حالت کلی همواره یک رشته یا آرایه به خروجی ارسال نمی‌شود. بلکه گاهی نیاز است نمونه‌ای از شیء و کلاس Response به view ارسال شود. برای ارسال یک نمونه‌ی کامل Resonse اعم از وضعیت HTTP و سربرگ‌ها (headers) می‌توان از دستور ()response استفاده کرد. به مثال زیر توجه کنید:

Route::get('home', function () {
    return response('Hello World', 200)
                  ->header('Content-Type', 'text/plain');
});

در این مثال عدد ۲۰۰ نمایانگر پیامی حاوی موفقیت در ارسال پاسخ نسبت به درخواست است. آرگومان دوم که به عنوان وضعیت HTTP‌ یا HTTP Status نامیده می‌شود یک سری اعداد استاندارد هستند که به هنگام موفقیت نسبت به ارسال پاسخ یا عدم موفقیت آن ارسال می‌شوند. عبارت header هم یک سربرگ به صفحه اضافه می‌کند که نوع خروجی که ارسال می‌شود را مشخص می‌کند مثلا در این مثال text/plain به معنای متن و text است.

همچنین می‌توان چندین سربرگ را به یک پاسخ پیوست نموده و به خروجی کاربر ارسال کرد:

return response($content)
            ->header('Content-Type', $type)
            ->header('X-Header-ROXO', 'Header Value')
            ->header('X-Header-Ask', 'Header Value');

جهت جلوگیری از کدنویسی بیش از حد می‌توان تمام سربرگ‌ها را در یک مجموعه با دستور withHeaders ارسال کرد:

return response($content)
           ->withHeaders([
                'Content-Type' => $type,
                'X-Header-ROXO' => 'Header Value',
                'X-Header-ASK' => 'Header Value',
]);

ارسال کوکی به خروجی

سوال اولی که برای عزیزان پیش می‌آید:‌ کوکی چیست؟ کوکی پیغامی‌ست که سرور به مرورگر کاربر ارسال می‌کند. معمولا این اطلاعات و پیام‌ها درون یک فایل text ذخیره شده و با هر بار مراجعه کاربر به وب‌سایت آن فایل اجرا شده و از پردازش بیش از حد جلوگیری می‌کند. اما اشکالاتی که کوکی‌ها دارند عبارتند از:

  1. کوکی‌ها در کامپیوترها به سادگی قابل دسترس هستند (مسئله‌ی امنیتی)
  2. کوکی‌ها در صورت عدم تنظیم سرور توسط توسعه‌دهنده، به زودی پاک می‌شوند (کاربر پسند نیست)

از آنجا که شیء resopnse متدهای فراوانی را به صورت زنجیره‌ای می‌پذیرد، می‌توان با استفاده از متد cookie اطلاعات ذخیره‌شده در cookie سرور را به خروجی کاربر view و در نهایت مرورگر آن ارسال کرد. برای مثال می‌خواهیم یک کوکی بسازیم و اطلاعات آن را به خروجی ارسال کنیم:

return response($content)
                ->header('Content-Type', $type)
                ->cookie('name', 'value', $minutes);

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

return response($content)
                ->header('Content-Type', $type)
                ->cookie($name, $value, $minutes, $path, $domain, $secure, $httpOnly)

تعریف این متغییرها به صورت زیر است:

name:

نام کوکی را تعیین می‌کند.

value:

این مقدار درون کامپیوتر کاربر ذخیره می‌شود. مثلا برای دسترسی به مقدار این کوکی از دستور []COOKIE_$‌ استفاده می‌شود.

minutes:

برای تعیین کردن مدت زمان فعال بودن کوکی مورد استفاده قرار می‌گیرد. یعنی مدت‌زمان انقضاء یک کوکی در این متغییر ذخیره می‌شود.

path:

مسیری که کوکی در آن قابل دسترس است. اگر این مقدار برابر '/' شود کوکی با وارد کردن آدرس مثلا http://www.roxo.ir/ در دسترس خواهد بود. اگر روی عبارت 'foo/' تنظیم شود کوکی در مسیر http://www.roxo.ir/foo در دسترس است.

domain:

دامنه‌ای که کوکی در آن قابل دسترس است.

secure:

تعیین می‌کند که کوکی تنها در بستر یک انتقال امن HTTPS صورت بگیرد. در صورتیکه این مقدار برابر TURE تنظیم شود، کوکی تنها در ارتباط HTTPS عمل می‌کند.

httponly:

اگر این مقدار برابر TURE تنظیم شود کوکی تنها در پروتکل HTTP در دسترس است. این‌بدین معنی‌ست که کوکی با اسکریپت‌نویسی قابل دسترس نیست. یعنی اگر شما به آدرس عبارتهایی را اضافه کنید تا وب سایت هرگز هک نخواهد شد و به‌نوعی از حملات XSS جلوگیری می‌کند.

کوکی‌ها و رمزنگاری آنها

به‌صورت پیش‌فرض تمام کوکی‌هایی که توسط لاراول تولید می‌شوند رمزگذاری و علامت‌گذاری می‌شوند. بنابراین آنها توسط کاربر قابل ویرایش و یا قابل خواندن نیستند. اگر شما بخواهید رمزگذاری روی کوکی‌ها و یا بخشی از کوکی‌ها را غیرفعال کنید باید از ویژگی except$ استفاده کنید تا میان‌افزار (Middleware) این فیلتر را برای شما انجام دهد. این میان‌افزار در مسیر \app\Http\Middleware موجود است و می‌توان در بخش protected مقدار except را به صورت زیر مشخص کرد:

<?php

namespace App\Http\Middleware;

use Illuminate\Cookie\Middleware\EncryptCookies as BaseEncrypter;

class EncryptCookies extends BaseEncrypter
{
    /**
     * The names of the cookies that should not be encrypted.
     *
     * @var array
     */
    protected $except = [
        'cookie_name',
    ];
}

در این‌صورت کوکی cookie_name رمزگذاری نمی‌شود.

View Responses پاسخ‌های ویو

نحوه‌ی دیگری از ارسال پاسخ به خروجی وجود دارد که می‌توان متد ()view در کلاس Response را بکار برد و علاوه بر اساس داده، خطا و همچنین سربرگهایی را نیز ارسال کرد:

return response()
            ->view('hello', $data, 200)
            ->header('Content-Type', $type);

درنظر دارید که اگر نخواهید از HTTP Status‌ استفاده کنید باید از روش ساده‌ی ()view که در ابتدای این فصل اشاره کردیم، بهره ببرید.

JSON Responses پاسخ‌های جیسون

کلمه‌ی JSON مخفف عبارت JavaScript Object Notation است. و به نوعی نمایش و تجزیه تحلیل داده‌ها را برای کاربر قابل فهم‌تر می‌کند.

متد json در کلاس Reponse به شما کمک می‌کند تا آرایه‌ها را به صورت JSON (با استفاده از تابع json_encode در PHP) نمایش دهید. همچنین این متد به‌صورت خودکار سربرگ Content-Type را برابر application/json قرار می‌دهد:

return response()->json([
    'name' => 'Abigail',
    'state' => 'CA'
]);

اگر شما تمایل داشته باشید که یک پاسخ JSONP تولید کنید باید متد json‌ را با متد withCallback‌ ترکیب کنید:

return response()
            ->json(['name' => 'Abigail', 'state' => 'CA'])
            ->withCallback($request->input('callback'));

تفاوت بین JSON و JSONP:

JSON‌ برای نمایش داده‌های داخلی وب سایت به‌کار گرفته می‌شود درحالیکه JSONP‌ (مخفف JavaScript Object Notation with Padding) برای نمایش داده‌های دریافتی از یک سرور خارجی مورد استفاده قرار می‌گیرد تا با وارد‌کردن اطلاعات JSON به یک تابع callBack‌ اطلاعات موردنظر را تجزیه و تحلیل کرده و به صورت JSON در اختیار کاربر قرار دهد.

فایل‌های قابل دانلود

متد download برای ارسال یک پاسخ که کاربر را ملزم به دانلود فایل از مرورگر می‌کند، مورد استفاده قرار می‌گیرد. متد download در حالت کلی ۳ پارامتر دارد که شامل pathToFile$ و name$ و headers$ است. پارامتر نخست pathToFile مسیر فایل موجود در سرور، پارامتر name نام فایلی که کاربر در کامپیوتر ذخیره می‌کند، می‌باشد و می‌توان سربرگ‌های HTTP را به عنوان پارامتر سوم به متد ارسال کرد:

return response()->download($pathToFile);

return response()->download($pathToFile, $name, $headers);

// یا نوشتن آنها به صورت Facade:
==============================
return Response::download($pathToFile);

return Response::download($pathToFile, $name, $headers);

پاسخ‌های مربوط به فایل‌ها

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

return response()->file($pathToFile);

return response()->file($pathToFile, $headers);

ساخت یک پاسخ دلخواه

در صورتیکه بخواهیم یک پاسخ دلخواه برای کنترلر یا مسیرهای خود ایجاد کنیم ابتدا باید به مسیر app/providers برویم و سپس داخل متد ()boot، روش موردنظر و یا دستورات را برای نوشتن یک پاسخ دلخواه لحاظ کنیم. به فرض مثال می‌خواهیم یک پاسخ با عنوان Macros ایجاد کنیم. فایل ResponseMacroServiceProvider را در مسیر app/providers ایجاد کرده و در متد ()boot آن دستورهای زیر را مینویسم:

<?php

namespace App\Providers;

use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Facades\Response;

class ResponseMacroServiceProvider extends ServiceProvider
{
    /**
     * Register the application's response macros.
     *
     * @return void
     */
    public function boot()
    {
        Response::macro('caps', function ($value) {
            return Response::make(strtoupper($value));
        });
    }
}

این متد هر آنچه که به عنوان ورودی بگیرد با حروف بزرگ چاپ می‌کند. حال شما می‌توانید ده‌ها متد برای خود نوشته و از آن به عنوان ارسال خروجی در برنامه‌های خود استفاده کنید. این متد در کنترلر یا مسیرها (routes) به‌صورت زیر قابل دسترس است:

return response()->caps('foo');

Redirect (تغییر مسیر)

برای تغییر مسیر یک کاربر به صفحه‌ای خاص پس از انجام عملیاتی خاص می‌توان از Redirect Facade استفاده کرد. راه‌های متفاوتی برای ایجاد یک نمونه‌ از کلاس RedirectResponse وجود دارد که در ذیل به آنها اشاره خواهیم کرد:

متد تغیر مسیر ()redirect

این متد به آدرس URI‌ای که درون آن نوشته می‌شود کاربر را ارجاع می‌دهد:

Route::get('about', function () {
    return redirect('roxo/dashboard');
});

متد ()back:

از این روش برای بازگرداندن کاربر به صفحه قبلی استفاده می‌شود. مثلا یک کاربر پس از پر کردن تمام فیلدهای یک فرم به صفحه‌ی قبلی آن باز می‌گردد و تمام اطلاعات درون فیلدها داخل session ذخیره می‌شود:

Route::post('user/login', function () {
    // Validate the request...

    return back()->withInput();
});

متد ()route به همراه ()redirect:

با استفاده از متد ()route‌ می‌توان کاربر را به یک مسیر دلخواه با استفاده از route ارجاع داد:

return redirect()->route('login');

همچنین اگر route شما دارای پارامتر بود می‌توان آن را به عنوان آرگومان دوم به متد ()route ارسال کرد:

// For a route with the following URI: profile/{id}

return redirect()->route('profile', ['id' => 1]);

متد ()action به همراه ()redirect:

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

return redirect()->action('HomeController@index');

و مجددا اگر اکشن کنترلر، پارامترهایی را به عنوان ورودی می‌گرفت، آن را به متد ()action‌ اضافه می‌کنیم:

return redirect()->action(
    'UserController@profile', ['id' => 1]
);

تغییر مسیر با استفاده از داده‌های Flashed Session:

گاهی برخی از اطلاعات ورودی در Session ذخیره می‌شود که به آنها Flashed Session Data گفته می‌شود. مثلا وقتی یک فرم با موفقیت ذخیره می‌شود پیغامی را به کاربر نمایش می‌دهیم. برای این‌کار به صورت زیر عمل می‌‎کنیم:

Route::post('user/profile', function () {
    // Update the user's profile...

    return redirect('dashboard')->with('status', 'Profile updated!');
});

حال اگر بخواهیم این پیام را در خروجی به کاربران نمایش دهیم از دستورهای زیر استفاده خواهیم کرد:

@if (session('status'))
    <div class="alert alert-success">
        {{ session('status') }}
    </div>
@endif

بسیار عالی، به شما عزیزان تبریک می‌گوییم. هم اکنون به تمام مباحث Request و Response یا همان درخواست و پاسخ مسلط شده‌اید و می‌توانید به دقت از آنها استفاده کرده تا اپلیکیشنی جذاب و انعطاف‌پذیر داشته باشید. از اینکه با ما همراه هستید از شما ممنونیم. در فصل بعدی وارد مبحث کار با دیتابیس و مدل‌ها خواهیم شد.

تمام فصل‌های سری ترتیبی که روکسو برای مطالعه‌ی دروس سری لاراول توصیه می‌کند:
نویسنده شوید
دیدگاه‌های شما (2 دیدگاه)

در این قسمت، به پرسش‌های تخصصی شما درباره‌ی محتوای مقاله پاسخ داده نمی‌شود. سوالات خود را اینجا بپرسید.

سارا
04 آذر 1398
عالیه توضیحات ساده و روان مرسی بابت زحمت و لطف خوبتون عشقین :)

در این قسمت، به پرسش‌های تخصصی شما درباره‌ی محتوای مقاله پاسخ داده نمی‌شود. سوالات خود را اینجا بپرسید.

عباسی
16 اردیبهشت 1397
خیلی پیچیده اش کردین ، اصلا قابل فهم نیست

در این قسمت، به پرسش‌های تخصصی شما درباره‌ی محتوای مقاله پاسخ داده نمی‌شود. سوالات خود را اینجا بپرسید.