مشخصات مقاله
-
2823
-
0.0
-
25424
-
0
-
0
آموزش HTTP Routing در لاراول
HTTP Routing (مسیردهی HTTP)
فهرست محتوا
- مسیردهی ساده با لاراول
-
پارامترهای Route
- پارامترهای الزامی
- پارامترهای اختیاری
- Regular Expression Constraint (محدود سازی پارامترها بر اساس عبارات با قاعده)
- فایده ی نام گذاری Route ها
-
قابلیت گروه بندی route ها (route groups)
- Middleware
- Namespace ها
- Route در زیردامنه
- پیشوند route ها
-
محافظت در برابر حملات CSRF
- مقدمه
- حذف URI ها
- X-CSRF-Token
- X-XSRF-Token
- Bind کردن مدل
- جعل متدهای ارسال فرم
- ارسال خطاهای با کد 404
مسیردهی ساده با لاراول
بیشتر route های اپلیکیشن خود را در فایل app/Http/routes.php قرار می دهیم که بارگذاری آن توسط کلاسApp\Providers\RouteServiceProvider انجام می گیرد. ساده ترین route های لاراول تنها یک URI (شناسه ی یکنواخت منبع) و یکClosure می پذیرند:
Route::get('/', function () {
return 'Hello World';
});
Route::post('foo/bar', function () {
return 'Hello World';
});
Route::put('foo/bar', function () {
//
});
Route::delete('foo/bar', function () {
//
});
ثبت یک route برای چندین متد HTTP (تعریف یک route برای پاسخی دهی به چند درخواست)
گاهی لازم می شود یک route ثبت و تعریف نمایید که همزمان به چند درخواست و متد HTTP پاسخ دهد. برای این منظور متد match را در Route فاساد (facade) به صورت زیر فراخوانی می کنیم:
Route::match(['get', 'post'], '/', function () {
return 'Hello World';
});
یا می توانید یک route معرفی کنید که به تمامی درخواست های HTTPبه وسیله ی متد any پاسخ می دهد:
Route::any('foo', function () {
return 'Hello World';
});
ایجاد URL های کامل برای Route مورد نظر
گاهی لازم است آدرس URL یک مسیر (route) را در سایت به نمایش بگذارید. این کار توسط تابع کمکی (helper) صورت می پذیرد (در واقع یکurl کامل برای مسیر/path معین اایجاد می کند):
$url = url('foo');
پارامترهای Route
پارامترهای الزامی
گاهی لازم است بخش هایی از url را به عنوان آرگومان دریافت کنید. به عنوان مثال، ممکن بخواهید ID کاربر را از URL دریافت کنید. این کار با تعریف پارامتر برای Route امکان پذیر می باشد:
Route::get('user/{id}', function ($id) {
return 'User '.$id;
});
می توانید هر تعداد پارامتر که route شما لازم دارد، تعریف نمایید:
Route::get('posts/{post}/comments/{comment}', function ($postId, $commentId) {
//
});
پارامترهای route همیشه داخل {} محصور می شوند. پارامترها در زمان اجرای route به داخل Closure پاس داده می شوند.
داخل پارامترهای route امکان استفاده از کاراکتر " - " وجود ندارد. بجای کاراکتر مزبور بایستی از زیرخط " _ " استفاده نمود.
پارامترهای اختیاری
گاهی ممکن است لازم باشد یک پارامتر route مشخص کنید، با این حال وجود آن پارامتر را در route خود اختیاری نمایید. برای این منظور کافی است کاراکتر ? را پس از اسم پارامتر درج نمایید:
Route::get('user/{name?}', function ($name = null) {
return $name;
});
Route::get('user/{name?}', function ($name = 'John') {
return $name;
});
Regular Expression Constraint (محدود سازی پارامترها بر اساس عبارات باقاعده)
می توان فرمت پارامترهای route را با استفاده از متد where در نمونه ی route با اعمال محدودیت فیلتر کرد. متد ذکر شده اسم پارامتر مورد نظر و یک regular expression به عنوان آرگومان می پذیرد که نحوه ی محدودسازی پارامتر را مشخص می کند:
Route::get('user/{name}', function ($name) {
//
})
->where('name', '[A-Za-z]+');
Route::get('user/{id}', function ($id) {
//
})
->where('id', '[0-9]+');
Route::get('user/{id}/{name}', function ($id, $name) {
//
})
->where(['id' => '[0-9]+', 'name' => '[a-z]+']);
تعریف الگوهای محدودسازی به صورت سراسری (Global Constraint)
اگر می خواهید یک پارامتر route معین همیشه توسط regular expression تصدیق اعتبار و محدود شود (آن پارامتر با الگوی مشخص شده در عبارات باقاعده تطابق داده شده و سپس بازیابی شود)، در آن صورت می توانید متد pattern را فراخوانی نمایید. شما می بایست pattern ها را در متد دیگری به نام boot تعریف کنید:
/**
* Define your route model bindings, pattern filters, etc.
*
* @param \Illuminate\Routing\Router $router
* @return void
*/
public function boot(Router $router)
{
$router->pattern('id', '[0-9]+');
parent::boot($router);
}
پس از تعریف pattern یا الگوی محدود سازی، آن pattern به واسطه ی اسم پارامتر مورد نظر به تمامی route ها (به صورت خودکار) اعمال می شود:
Route::get('user/{id}', function ($id) {
// Only called if {id} is numeric.
});
فایده ی نام گذاری Route ها
با نام گذاری route ها به راحتی می توانید URL ها یا redirect هایی برای یک route معین تولید کنید (با تخصیص یک اسم به route می توان به آسانی برای آن url ایجاد کرد و از آن برای redirect کردن بهره گرفت). به منظور تخصیص نام مشخص به یک route می بایست به هنگام تعریف آن، از کلیدواژه ی as در آرایه استفاده کنید:
Route::get('user/profile', ['as' => 'profile', function () {
//
}]);
همچنین می توانید برای action های controller از امکان نام گذاری route ها استفاده کنید (برای هدایت یک route به اکشن و کنترلر به صورت زیر اقدام نمایید):
Route::get('user/profile', [
'as' => 'profile', 'uses' => 'UserController@showProfile'
]);
بجای مشخص کردن اسم route در تعریف آرایه ی متعلق به آن route، می توانید متد name را به انتهای تعریف route متصل کنید:
Route::get('user/profile', 'UserController@showProfile')->name('profile');
Route Groups (قابلیت گروه بندی روت ها) و route های نام گذاری شده
اگر بخواهید از route groups (قابلیت گروه بندی روت ها) استفاده کنید، در آن صورت می توانید یک as در آرایه ای که مقدار خصیصه (attribute) group هست، مشخص کنید. این کار به شما امکان می دهد تا یک پیشوند (اسم route) مشترک برای تمامی route های عضو گروه تنظیم کنید:
Route::group(['as' => 'admin::'], function () {
Route::get('dashboard', ['as' => 'dashboard', function () {
// Route named "admin::dashboard"
}]);
});
ایجاد URL هایی برای Route های نام گذاری شده
پس از تخصیص نام مشخص به یک route، می توانید اسم آن route را به هنگام تولید URL یا redirect کردن از طریق تابع route، بکار ببرید (به عبارت روشن تر می توانید از اسم route برای تولید URL و redirect کردن استفاده کنید):
$url = route('profile');
$redirect = redirect()->route('profile');
اگر route پارامترهایی داشته باشد، در آن صورت می توانید پارامترها را به عنوان آرگومان دوم به متد route ارسال کنید. پارامترهای مشخص شده به صورت خودکار داخل URL قرار داده می شوند:
Route::get('user/{id}/profile', ['as' => 'profile', function ($id) {
//
}]);
$url = route('profile', ['id' => 1]);
قابلیت گروه بندی route ها (Route Groups)
Route Groups به شما این امکان را می دهد تا attribute هایی نظیر middleware و namespace را بین چندین route به اشتراک بگذارید تا دیگر نیاز نباشد آن attribute ها را به ازای هر route جدا تعریف کنید (روت ها خصیصه های مشترکی همچون namespace و middlewareدارند. می توان بجای اینکه هر یک از این خصیصه ها را برای هر route به صورت جداگانه تعریف کنید، می توانید کلیه ی خصیصه ها را در یک گروه تعریف نمایید. آنگاه خصیصه های تعریف شده در گروه به تمامی روت ها اعمال می شوند) attribute های مشترک به صورت یک آرایه تعریف شده و به عنوان پارامتر اول به متد Route::group ارسال می شوند.
Middleware
جهت انتساب middleware به تمامی route های عضو گروه، می توانید از کلید middleware در آرایه ی attribute group استفاده کنید.middleware ها دقیقا به ترتیبی که در آرایه تعریف شده اند به route ها عضو گروه اعمال و اجرا می شوند:
Route::group(['middleware' => 'auth'], function () {
Route::get('/', function () {
// Uses Auth Middleware
});
Route::get('user/profile', function () {
// Uses Auth Middleware
});
});
Namespace ها
یکی دیگر از موارد کاربرد route group ها، تخصیص یک namespace واحد PHP به یک گروه از کنترلرها است. می توانید پارامترnamespace را در آرایه ی group attribute استفاده کنید تا namespace برای تمامی کنترولر ها در آن گروه مشخص شود:
Route::group(['namespace' => 'Admin'], function()
{
// Controllers Within The "App\Http\Controllers\Admin" Namespace
Route::group(['namespace' => 'User'], function()
{
// Controllers Within The "App\Http\Controllers\Admin\User" Namespace
});
});
یادآور می شویم که RouteServiceProvider فایل routes.php را به صورت پیش فرض داخل گروه namespace بارگذاری می کند. این امر به شما اجازه می دهد تا route های کنترلر را بدون نیاز به مشخص کردن پیشوند کامل App\Http\Controllers معرفی و ثبت کنید. بنابراین ما فقط بایستی آن بخشی از namespace را مشخص کنیم که پس از (namespace root) App\Http\Controllers می آید (از آنجایی کهRouteServiceProvider پیش فرض فایل routes.php را در namespace بارگذاری می کند، می توان Controller ها را بدون معرفی فضای نام (namespace) مربوط به آن داخل route های یک گروه تعریف نمود ).
Route در زیردامنه
از route group ها همچنین می توان برای مسیردهی (routing) در زیردامنه های بهره گرفت و به پارامترهای wildcard دسترسی داشت. می توان به subdomain ها پارامتر route اختصاص داد که به شما امکان می دهد تا بخشی از زیردامنه را برای استفاده در route یا کنترلر به عنوان پارامتر دریافت کنید. زیردامنه را به وسیله ی کلید domain در آراِیه مشخص می کنیم. در زیر یک subdomain برای گروهی از Route ها اعلان می کنیم:
Route::group(['domain' => '{account}.myapp.com'], function () {
Route::get('user/{id}', function ($account, $id) {
//
});
});
اعمال پیشوند به Route ها
با استفاده از پارامتر prefix می توان به هر route در گروه یک پیشوند اعمال کرد. برای مثال ممکن است بخواهید به تمامی URI های route عضو گروه یک پیشوند admin اعمال کنید (می توان به صورت زیر پیشوند URL برای Route ای که عضو یک گروه است، تعریف کرد):
Route::group(['prefix' => 'admin'], function () {
Route::get('users', function () {
// Matches The "/admin/users" URL
});
});
همچنین می توانید به وسیله ی پارامتر prefix پارامترهای یکسانی را برای route های گروه بندی شده مشخص کرد:
Route::group(['prefix' => 'accounts/{account_id}'], function () {
Route::get('detail', function ($account_id) {
// Matches The accounts/{account_id}/detail URL
});
});
محافظت در برابر حملات CSRF
مقدمه
لاراول به شما این امکان را می دهد تا برنامه ی خود را از خطر جعل درخواست های میان وبگاهی (Cross-site request forgeries) محافظت نمایید. CSRF یک نوع سوء استفاده است که در آن دستورات غیرمجاز از طرف یا به نیابت از یک کاربر تصدیق هویت شده (authenticate) اجرا می شوند. به عبارتی دیگر در این نوع از حملات کاربری که در یک اپلیکیشن تحت وب ثبت ورود (لاگین) کرده است را مجبور به فرستادن یک درخواست به آن نرمافزار تحت وب آسیبپذیر میکنند تا عملی که میخواهند را انجام دهد.
لاراول به صورت اتوماتیک یک توکن CSRF ویژه ی هر user session فعال (درخواست)که برنامه مدیریت می کند، ایجاد می نماید. با استفاده از این توکن می توان دریافت آیا کاربری که درخواست را به برنامه می دهد همان کاربر تصدیق هویت شده است یا خیر. برای ایجاد یک فیلد دریافت کننده ی ورودی (input field) که نوع آن hidden و اسمش (مقدار خصیصه ی name آن) _token باش، می توانید از تابع کمکیcsrf_field بهره بگیرید:
<?php echo csrf_field(); ?>
متد کمکی csrf_field کد HTML زیر را تولید می کند:
<input type="hidden" name="_token" value="<?php echo csrf_token(); ?>">
ایجاد یک توکن در فرم به وسیله ی Blade:
{{ csrf_field() }}
نیازی نیست به صورت دستی توکن CSRF را برای درخواست های POST، PUT یا DELETE تصدیق و بررسی کنید. VerifyCsrfTokenبررسی می کند آیا توکن موجود در ورودی درخواست با توکن ذخیره شده در session مطابقت دارد یا خیر.
مستثنی کردن URI ها از (محافظت در برابر حملات CSRF)CSRF Protection
گاهی لازم است یک مجموعه از URI ها را از محافظت در برابر حملات CSRF مستثنی نمایید. برای مثال، اگر از Stripe برای پردازش payment ها استفاده کنید و در این میان از سیستم webhook آن ها نیز استفاده می کنید، در آن صورت می بایست webhook handler route را از CSRF protection لاراول حذف (مستثنی) نمایید.
می توانید URI هایی را با افزودن آن ها به پراپرتی$except از میان افزار (middleware) VerifyCsrfToken، از CSRF protectionمستثنی نمایید:
<?php
namespace App\Http\Middleware;
use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken as BaseVerifier;
class VerifyCsrfToken extends BaseVerifier
{
/**
* The URIs that should be excluded from CSRF verification.
*
* @var array
*/
protected $except = [
'stripe/*',
];
}
X-CSRF-TOKEN
VerifyCsrfToken علاوه بر اینکه بررسی می کند آیا توکن CSRF به عنوان پارامتر متد POST ارسال شده، همچنین هدر درخواست X-CSRF-TOKEN را چک می کند. به عنوان مثال می توانستیم توکن را در یک تگ meta اچ تی ام ال ذخیره کنید:
<meta name="csrf-token" content="{{ csrf_token() }}">
پس از ایجاد تگ meta، می توانید به یک کتابخانه همچون jQuery دستور دهید توکن را به تمامی هدرها ضمیمه کند. این کار امکان محافظت آسان و بهینه را برای اپلیکیشن های مبتنی بر AJAX در برابر حملات CSRF به ارمغان می آورد.
$.ajaxSetup({
headers: {
'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
}
});
X-XSRF-TOKEN
لاراول توکن CSRF را در کوکی XSRF-TOKEN نیز ذخیره می کند. می توانید مقدار کوکی را برای تنظیم و مقداردهی هدر X-XSRF-TOKEN مورد استفاده قرار دهید. برخی از کتابخانه های جاوااسکریپت مانند AngularJS این کار را به صورت خودکار و بدون نیاز به دخالت شما انجام می دهند. به عبارت دیگر در بیشتر موارد نیازی نیست این مقدار را به صورت دستی بکار ببرید.
Bind کردن مدل
Model binding در لاراول روشی آسان برای تزریق نمونه های کلاس در route ها فراهم می آورد. به عنوان مثال بجای اینکه تنها ID کاربر را تزریق کنید، می توانید کل نمونه ی کلاس User را که با ID معین منطبق است، تزریق نمایید.
ابتدا با استفاده از متد model، کلاس لازم برای ارسال به متد به عنوان پارامتر را مشخص نمایید. شایان ذکر است که model binding های خود را می بایست در متد RouteServiceProvider::boot تعریف کنید:
Bind کردن یک پارامتر به یک مدل
public function boot(Router $router)
{
parent::boot($router);
$router->model('user', 'App\User');
}
در مرحله ی بعد یک route تعریف می کنیم که حاوی پارامتر {user} باشد:
$router->get('profile/{user}', function(App\User $user) {
//
});
چون که پارامتر {user} را به مدل App\User متصل (bind) کردیم، یک نمونه از کلاس User به داخل route تزریق می شود. بنابراین یک درخواست به profile/1 در واقع نمونه ی User را که ID آن 1 می باشد به درون route مورد نظر تزریق می کند.
در صورت یافت نشدن مورد منطبق با نمونه ی مدل در پایگاه داده، یک خطا با کد 404 به صورت خودکار به نمایش در می آید.
چنانچه تمایل دارید مکانیزم " not found " اختصاصی خود را تعریف نمایید، در آن صورت می بایست یک Closure به عنوان آرگومان سوم به متدmodel ارسال کنید:
$router->model('user', 'App\User', function() {
throw new NotFoundHttpException;
});
در صورت تمایل به استفاده از منطق resolution اختصاصی خود، می بایست متد Route::bind را بکار ببرید. Closure ای که به متد bind ارسال می کنید در واقع مقدار بخش URI را دریافت می کند و شما می بایست یک نمونه از کلاسی که می خواهید داخل route تزریق شود را برگردانید:
$router->bind('user', function($value) {
return App\User::where('name', $value)->first();
});
جعل متدهای ارسال فرم
فرم های HTML از متدهای PUT، PATCH یا DELETE پشتیبانی نمی کند. بنابراین به هنگام تعریف روت های PUT، PATCH یا DELETEاز یک فرم HTML (برای آدرسی درهی یا روت کردن درخواست از این دست)، می بایست یک فیلد از نوع hidden که خصیصه ی name آن بر روی_method تنظیم شده به فرم اضافه نمایید. مقدار ارسالی با فیلد نام برده (_method) سپس به عنوان متد درخواست HTTP مورد استفاده قرار می گیرد:
>form action="/foo/bar" method="POST">
>input type="hidden" name="_method" value="PUT">
>input type="hidden" name="_token" value="{{ csrf_token() }}">
>/form>
برای ایجاد فیلد ورودی از نوع hidden می توانید تابع کمکیmethod_field را بکار ببرید:
<?php echo method_field('PUT'); ?>
می توان همین کار را به صورت بهینه تر با استفاده از Blade انجام داد:
{{ method_field('PUT') }}
ارسال خطاهای با کد 404
در کل دو روش برای فعال کردن و فرستادن خطاهای 404 به صورت دستی از یک route وجود دارد. در روش اول از تابع کمکی abort استفاده می کنیم. تابع کمکی ذکر شده تنها یک خطای Symfony\Component\HttpFoundation\Exception\HttpException با کد وضعیت مشخص (status code) که به عنوان پارامتر به آن ارسال شده، ایجاد می کند:
abort(404);
در روش دوم خطا را به واسطه ی یک نمونه از کلاس Symfony\Component\HttpKernel\Exception\NotFoundHttpException به صورت دستی ایجاد می کنیم.