مسیریابی
مسیرها در فینچ با استفاده از اشیاء FinchRoute که درون یک تابع مسیریابی جمعآوری شدهاند، تعریف میشوند. تابع مسیریابی با app.addRouting() ثبت شده و برای هر درخواست ورودی فراخوانی میشود.
تابع مسیریابی
import 'package:finch/finch_route.dart';
import '../controllers/home_controller.dart';
final homeController = HomeController();
Future<List<FinchRoute>> getWebRoute() async {
return [
FinchRoute(
key: 'root',
path: '/',
methods: Methods.ONLY_GET,
index: homeController.index,
),
];
}
آن را در app.dart ثبت کنید:
app.addRouting(getWebRoute);
پارامترهای FinchRoute
| پارامتر | نوع | توضیح |
|---|---|---|
key |
String |
شناسه منحصربهفرد مسیر. برای تولید URL استفاده میشود ($e.routeUrl('key')) |
path |
String |
بخش مسیر URL. از {param} یا :param و wildcard * پشتیبانی میکند |
methods |
List<String> |
متدهای HTTP مجاز. از ثابتهای Methods استفاده کنید |
index |
Future<String> Function()? |
تابع handler (بدون پارامتر rq؛ از Context.rq یا متد controller استفاده کنید) |
controller |
Controller? |
نمونه controller. وقتی index تنظیم نشده باشد، index() آن فراخوانی میشود |
children |
List<FinchRoute> |
مسیرهای فرزند تودرتو. مسیر نسبت به والد است |
extraPath |
List<String> |
مسیرهای اضافی که به همین مسیر map میشوند |
auth |
AuthController? |
نگهبان احراز هویت برای این مسیر |
permissions |
List<String> |
رشتههای دسترسی که پس از auth بررسی میشوند |
middlewares |
List<Middleware> |
زنجیره middleware که قبل از handler اجرا میشود |
hosts |
List<String> |
محدود کردن به hostnameهای خاص (['*'] = همه) |
ports |
List<int> |
محدود کردن به پورتهای خاص ([] = همه) |
params |
Map<String, dynamic> |
پارامترهای پیشفرض قالب |
excludePaths |
List<String> |
زیرمسیرهایی که از این مسیر مستثنا شدهاند |
apiDoc |
Future<ApiDoc>? Function()? |
مستندات API برای Swagger |
widget |
String |
مسیر یک فایل قالب برای رندر مستقیم، بدون نیاز به controller یا index (به بخش «مسیرهای Widget» در پایین مراجعه کنید) |
title |
String |
عنوان صفحه، در قالبها بهصورت {{ $e.pageTitle }} در دسترس است |
از میان widget، index یا controller فقط یکی را برای هر مسیر استفاده کنید. اگر بیش از یکی تنظیم شود، ابتدا widget بررسی میشود، سپس index، و در نهایت controller.index() بهعنوان جایگزین.
متدهای HTTP
// ثابتهای دسترسی از کلاس Methods:
Methods.ONLY_GET // ['GET']
Methods.ONLY_POST // ['POST']
Methods.ONLY_PUT // ['PUT']
Methods.ONLY_DELETE // ['DELETE']
Methods.GET_POST // ['POST', 'GET']
Methods.ALL // تمام متدهای HTTP استاندارد
// یا یک لیست سفارشی:
methods: [Methods.GET, Methods.POST, Methods.DELETE]
پارامترهای مسیر
از دستور {name} برای ضبط بخشهای URL استفاده کنید. آنها را در controller با rq.getParam('name') بخوانید:
FinchRoute(
key: 'users.show',
path: 'users/{id}', // یا 'users/:id'
methods: Methods.ONLY_GET,
index: userController.show,
),
نکته: بخشهای پویای مسیر میتوانند با استفاده از هر دو syntax {param} یا :param تعریف شوند. هر دو پشتیبانی میشوند و عملکرد یکسانی دارند.
// در UserController:
Future<String> show() async {
var id = rq.getParam('id');
return rq.renderData(data: {'id': id});
}
مسیرهای Widget
هنگامی که یک مسیر فقط نیاز به رندر یک قالب دارد، بدون هیچ منطق سفارشیای، بهجای نوشتن یک controller، widget را به مسیر یک فایل قالب تنظیم کنید:
FinchRoute(
key: 'about',
path: 'about',
title: 'About Us',
widget: 'pages/about',
),
widget قبل از index و controller بررسی میشود و — برخلاف آن دو — منتظر اجرای auth مخصوص به خودِ این مسیر نمیماند؛ فقط دسترسیهای (permissions) بهارثرسیده از یک مسیر والد که از قبل احراز هویت شده بررسی میشوند. اگر یک مسیر widget به احراز هویت مخصوص به خودش نیاز دارد، بهجای تکیه بر فیلد auth خودِ مسیر widget، auth را به یک مسیر والد ضمیمه کنید، یا از index/controller استفاده کنید تا بررسی auth بهصورت عادی اجرا شود.
مسیرهای Wildcard و استثناها
مسیری که با * پایان مییابد، خودِ مسیر و هر زیرمسیر زیر آن را match میکند — این برای handlerهای catch-all مانند fallback یک single-page app یا یک پراکسی فایل عمومی مفید است:
FinchRoute(
key: 'spa.fallback',
path: 'app/*',
index: homeController.spaIndex,
excludePaths: ['app/api'], // همچنان برای این زیرمسیر به مسیرهای دیگر واگذار میشود
),
excludePaths زیرمسیرهایی را فهرست میکند که نباید توسط wildcard بلعیده شوند، بنابراین مسیرهای خاصتر (یا فایلهای استاتیک) زیر wildcard همچنان قابلدسترسی باقی میمانند.
مسیرهای تودرتو (children)
مسیرهای فرزند نسبت به مسیر والد هستند:
FinchRoute(
key: 'example',
path: 'example',
index: homeController.redirectToRoot,
children: [
FinchRoute(
key: 'example.form.get',
path: 'form',
methods: Methods.ONLY_GET,
index: homeController.exampleForm,
),
FinchRoute(
key: 'example.form.post',
path: 'form',
methods: Methods.ONLY_POST,
index: authController.loginPost,
),
FinchRoute(
key: 'example.panel',
path: 'panel',
methods: Methods.ALL,
auth: authController,
permissions: ['admin'],
index: homeController.exampleAuth,
),
],
),
مسیرهای اضافی
extraPath پیشوندهای URL اضافی را به همان مسیر و فرزندان آن map میکند:
FinchRoute(
key: 'root.mysql',
path: 'example/mysql',
extraPath: ['api/example/mysql'],
methods: Methods.GET_POST,
index: homeController.exampleMysql,
),
ایجاد چندین مسیر مشابه
FinchRoute.makeList چندین مسیر ایجاد میکند که پیکربندی یکسانی دارند اما فقط در path تفاوت دارند — مناسب برای نامهای مستعار (alias) یا endpointهای تقریباً یکسان:
var routes = FinchRoute.makeList(
paths: ['users', 'members', 'people'],
methods: Methods.ONLY_GET,
controller: UserController(),
auth: AppAuthController(),
permissions: ['user.read'],
key: 'user.alias', // تبدیل میشود به 'user.alias.1'، 'user.alias.2'، 'user.alias.3'
);
Middleware
یک یا چند نمونه Middleware را به یک مسیر (یا یک زنجیره fluent .middleware()) ضمیمه کنید تا منطق فیلتر کردن درخواست قبل از اجرای handler اجرا شود — لاگگیری، بررسی هدر، rate limiting، CORS و دغدغههای cross-cutting مشابه. برای API کامل و ترتیب اجرا نسبت به auth و permissions به Middleware Guide مراجعه کنید.
فیلتر Host و Port
یک مسیر را به hostها یا پورتهای خاص محدود کنید:
FinchRoute(
key: 'root.localhost',
path: 'example/host',
hosts: ['localhost'],
ports: [80, 8085],
index: homeController.renderLocalhost,
methods: Methods.ALL,
),
FinchRoute(
key: 'root.host',
path: 'example/host',
ports: [80, 8085],
hosts: ['127.0.0.1'],
index: homeController.render127001,
methods: Methods.ALL,
),
نگهبان احراز هویت
برای محافظت از یک مسیر، یک AuthController ضمیمه کنید. اگر auth() مقدار false برگرداند، درخواست قبل از رسیدن به handler رد میشود:
FinchRoute(
key: 'admin.panel',
path: 'admin/panel',
auth: AppAuthController(),
permissions: ['admin'],
index: adminController.panel,
),
برای جزئیات پیادهسازی به Auth Controller مراجعه کنید.
کش مسیر
از پسوند .cache() برای کش کردن پاسخها استفاده کنید:
FinchRoute(
key: 'root.route',
path: 'example/route',
methods: Methods.ONLY_GET,
index: homeController.exampleRoute,
).cache(
cacheDuration: Duration(minutes: 10),
cacheType: [CacheParam.path, CacheParam.method, CacheParam.language],
cacheSource: CacheSource.file,
),
برای جزئیات کامل به Route Cache مراجعه کنید.
تولید URL از کلیدهای مسیر
در قالبها، از $e.routeUrl('key') برای تولید URL استفاده کنید:
<a href="{{ $e.routeUrl('example.panel') }}">Panel</a>
<a href="{{ $e.routeUrl('users.show', {'id': user.id}) }}">View User</a>
دریافت همه مسیرها
فهرست کامل مسیرها را در زمان اجرا دریافت کنید:
var routes = await app.getAllRoutes();
نحوه عملکرد تطابق
برای هر درخواست ورودی، فینچ فهرست مسیریابی که با app.addRouting() ثبت کردهاید را بهترتیب، از بالا به پایین، پیمایش میکند:
- فیلتر Host/Port — اگر
hostsیاportsبا درخواست مطابقت نداشته باشند، مسیر بلافاصله رد میشود. - تطابق Path — مقدار
pathمسیر (بههمراه هرextraPath) با مسیر درخواست مقایسه میشود و در این فرآیند جایگزینهای{param}/:paramو پسوندهای wildcard*تفکیک میشوند. - اگر path مطابقت داشته باشد اما
methodsشامل متد HTTP درخواست نباشد، مسیر بهعنوان یافتنشده در نظر گرفته میشود و تطابق به مسیر بعدی ادامه مییابد. - اگر مسیر دارای
childrenباشد، بهمحض تطابق پیشوند مسیر والد، فینچ به داخل آنها بازگشت (recurse) میکند —authوmiddlewaresروی والد همچنان ابتدا اجرا میشوند و برای هر فرزند اعمال میشوند. - اولین مسیری که بهطور کامل مطابقت دارد (path، method، host، port، auth، permissions، middleware) درخواست را مدیریت میکند؛ هیچ مسیر دیگری امتحان نمیشود.
- اگر هیچچیزی مطابقت نداشته باشد، فینچ به سرویسدهی یک فایل استاتیک از
publicDirبرمیگردد، و اگر آن فایل هم وجود نداشته باشد404برمیگرداند.
از آنجا که تطابق بهصورت اولین-تطابق-برنده است، ترتیب اهمیت دارد: مسیرهای خاصتر را قبل از مسیرهای wildcard گستردهتری که ممکن است آنها را ببلعند قرار دهید.