Middleware
Middleware مکانیزمی برای بازرسی و فیلتر کردن درخواستهای HTTP پیش از رسیدن آنها به route handler شما فراهم میکند.
Middleware چیست؟
Middleware در فینچ کلاسی است که بین درخواست HTTP ورودی و route handler شما (کنترلر یا تابع index) قرار میگیرد. این کلاس به شما اجازه میدهد منطقی را پیش از رسیدن درخواست به کنترلرتان اجرا کنید — برای مثال، بررسی هدرها، لاگگیری درخواستها، اعتبارسنجی توکنها، تغییر پارامترهای درخواست، یا مسدود کردن دسترسی غیرمجاز.
هر کلاس middleware باید از کلاس انتزاعی Middleware ارثبری کند و متد handle() را پیادهسازی کند.
Middleware چگونه کار میکند
وقتی درخواستی با مسیری که middleware به آن ضمیمه شده مطابقت پیدا کند، فینچ pipeline میانافزار (middleware) را بهترتیب پیش از عبور دادن درخواست به کنترلر یا تابع index اجرا میکند. جریان به این شکل است:
- تطابق الگوی مسیر (route pattern matching)
- اعتبارسنجی متد HTTP، host و port
- بررسی احراز هویت (
auth) - اجرای Middleware (متد
handle()هر middleware را بهترتیب اجرا میکند) - بررسی مجوز (permission)
- اجرای کنترلر/index
مقادیر بازگشتی
متد handle() یک Future<String?> برمیگرداند:
- بازگرداندن
null— درخواست عبور میکند و به middleware بعدی یا به route handler ادامه مییابد. - بازگرداندن یک
Stringغیرخالی — درخواست مسدود میشود. pipeline میانافزار متوقف میشود و مسیر match نمیشود.
اگر چندین middleware به یک مسیر ضمیمه شده باشند، آنها بهترتیب (sequentially) اجرا میشوند. اگر هر middlewareای یک رشته غیرخالی برگرداند، بقیه middlewareها نادیده گرفته میشوند و درخواست رد میشود.
ساخت یک Middleware
برای ساخت یک middleware، از کلاس Middleware ارثبری کنید و متد handle() را override کنید:
import 'package:finch/finch_route.dart';
class MyMiddleware extends Middleware {
@override
Future<String?> handle() async {
// منطق شما اینجا
return null; // اجازه ادامه درخواست
}
}
دسترسی به درخواست
درون یک middleware، میتوانید با استفاده از rq (که توسط کلاس پایه Middleware فراهم شده) به درخواست جاری دسترسی داشته باشید:
class LogMiddleware extends Middleware {
@override
Future<String?> handle() async {
print('Request: ${rq.method} ${rq.uri.path}');
return null;
}
}
افزودن پارامترها
میتوانید داده را از یک middleware با استفاده از rq.addParam() به درخواست تزریق کنید. این داده برای کنترلرها و قالبهای پاییندستی (downstream) در دسترس خواهد بود:
class TestMiddleware extends Middleware {
@override
Future<String?> handle() async {
rq.addParam('middleware', 'Test Middleware Active');
return null;
}
}
مسدود کردن یک درخواست
برای جلوگیری از رسیدن درخواست به route handler، یک رشته غیرخالی برگردانید:
class ApiKeyMiddleware extends Middleware {
@override
Future<String?> handle() async {
// rq.headers شیء HttpHeaders دارت را برمیگرداند
// یک مقدار هدر خاص را با ['header-name']?.first بخوانید
final apiKey = rq.headers['x-api-key']?.first ?? '';
if (apiKey != 'my-secret-key') {
rq.renderError(401, message: 'Invalid API key');
return 'Unauthorized'; // درخواست را مسدود میکند
}
return null; // اجازه ادامه درخواست
}
}
ضمیمه کردن Middleware به مسیرها
دو روش برای ضمیمه کردن middleware به یک مسیر وجود دارد.
۱. استفاده از Constructor
یک لیست از نمونههای middleware را به پارامتر middlewares در FinchRoute پاس دهید:
final testMiddleware = TestMiddleware();
final logMiddleware = LogMiddleware();
FinchRoute(
path: '/info',
index: homeController.info,
middlewares: [testMiddleware, logMiddleware],
);
۲. استفاده از Fluent API
از متد .middleware() برای زنجیر کردن middleware به یک مسیر استفاده کنید:
FinchRoute(
path: '/info',
index: homeController.info,
).middleware(TestMiddleware()).middleware(LogMiddleware());
Middleware روی مسیرهای والد
وقتی middleware به یک مسیر والد که دارای children است ضمیمه شود، middleware پیش از پردازش هر مسیر فرزند اجرا میشود. این به شما اجازه میدهد منطق مشترک (مانند احراز هویت یا لاگگیری) را روی کل یک گروه از مسیرها اعمال کنید:
FinchRoute(
path: '/admin',
middlewares: [AdminMiddleware()],
children: [
FinchRoute(
path: '/dashboard',
index: adminController.dashboard,
),
FinchRoute(
path: '/users',
index: adminController.users,
),
],
);
در این مثال، AdminMiddleware پیش از هر درخواست به /admin/dashboard یا /admin/users اجرا خواهد شد.
مثالهای عملی
Middleware محدودسازی نرخ (Rate Limiting)
class RateLimitMiddleware extends Middleware {
static final Map<String, List<DateTime>> _requests = {};
final int maxRequests;
final Duration window;
RateLimitMiddleware({
this.maxRequests = 100,
this.window = const Duration(minutes: 1),
});
@override
Future<String?> handle() async {
final ip = rq.clientIP;
final now = DateTime.now();
_requests[ip] = (_requests[ip] ?? [])
..removeWhere((t) => now.difference(t) > window)
..add(now);
if (_requests[ip]!.length > maxRequests) {
rq.renderError(429, message: 'Too many requests');
return 'Rate limit exceeded';
}
return null;
}
}
Middleware CORS
class CorsMiddleware extends Middleware {
final String allowedOrigin;
CorsMiddleware({this.allowedOrigin = '*'});
@override
Future<String?> handle() async {
rq.response.headers.add('Access-Control-Allow-Origin', allowedOrigin);
rq.response.headers.add('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
rq.response.headers.add('Access-Control-Allow-Headers', 'Content-Type, Authorization');
return null;
}
}
Middleware حالت تعمیر (Maintenance Mode)
class MaintenanceMiddleware extends Middleware {
final bool isEnabled;
MaintenanceMiddleware({this.isEnabled = false});
@override
Future<String?> handle() async {
if (isEnabled) {
rq.renderError(503, message: 'Service is under maintenance');
return 'Maintenance mode';
}
return null;
}
}
Middleware در برابر Auth Controller
| ویژگی | Middleware | Auth Controller |
|---|---|---|
| هدف | فیلتر کردن درخواست بهصورت general-purpose | احراز هویت و مجوزدهی |
| ترتیب اجرا | پس از auth، پیش از permissions | پیش از middleware |
| نوع بازگشتی | Future<String?> |
Future<bool> |
| امکان تغییر درخواست | بله (rq.addParam) |
بله |
| امکان رندر خطا | بله | بله |
| چند مورد در هر مسیر | بله (لیست) | یک مورد در هر مسیر |
| به ارثرسیده توسط فرزندان | بله | بله |
از Auth Controller زمانی استفاده کنید که به احراز هویت کامل و مدیریت session نیاز دارید. از Middleware برای هر چیز دیگری استفاده کنید — لاگگیری، اعتبارسنجی هدر، محدودسازی نرخ (rate limiting)، CORS، feature flagها و غیره.
مثال کامل
در ادامه یک مثال کامل از استفاده از middleware در یک اپلیکیشن فینچ آمده است:
۱. ساخت کلاسهای middleware:
// lib/middleware/test_middleware.dart
import 'package:finch/finch_route.dart';
class TestMiddleware extends Middleware {
@override
Future<String?> handle() async {
rq.addParam('middleware', 'Test Middleware Active');
return null;
}
}
۲. ثبت middleware در مسیرهای شما:
// lib/route/web_route.dart
import '../middleware/test_middleware.dart';
final testMiddleware = TestMiddleware();
List<FinchRoute> routes = [
FinchRoute(
key: 'root.info',
path: 'info',
extraPath: ['api/info'],
index: homeController.info,
middlewares: [testMiddleware],
),
];
۳. دسترسی به داده middleware در کنترلر شما:
class HomeController extends Controller {
Future<String> info() async {
final middlewareParam = rq.get('middleware');
return rq.renderString(text: 'Middleware says: $middlewareParam');
}
}
مرجع API
Middleware (کلاس انتزاعی)
| عضو | نوع | توضیح |
|---|---|---|
rq |
Request (getter) |
دسترسی به شیء درخواست HTTP جاری |
handle() |
Future<String?> |
برای پیادهسازی منطق middleware override کنید. برای عبور null و برای مسدود کردن یک رشته غیرخالی برگردانید. |
اعضای Middleware در FinchRoute
| عضو | نوع | توضیح |
|---|---|---|
middlewares |
List<Middleware> |
لیستی از نمونههای middleware ضمیمهشده به مسیر |
middleware(m) |
FinchRoute |
متد fluent برای افزودن یک middleware؛ مسیر را برای زنجیرهسازی (chaining) برمیگرداند |
handleMiddlewares() |
Future<bool> |
تمام middlewareها را بهترتیب اجرا میکند؛ اگر همه موفق بودند true برمیگرداند |