Middleware
中间件提供了一种在 HTTP 请求到达路由处理程序之前对其进行检查和过滤的机制。
What is Middleware?
Finch 中的中间件是一个位于传入 HTTP 请求与你的路由处理程序(控制器或 index 函数)之间的类。它允许你在请求到达控制器之前运行逻辑——例如检查请求头、记录请求日志、校验令牌、修改请求参数,或阻止未授权的访问。
每个中间件类都必须继承抽象类 Middleware 并实现 handle() 方法。
How Middleware Works
当一个请求匹配到附加了中间件的路由时,Finch 会在将请求传递给控制器或 index 函数之前,按顺序执行中间件流水线。整体流程如下:
- 路由模式匹配
- HTTP 方法、主机和端口校验
- 身份验证检查(
auth) - 中间件执行(依次运行每个中间件的
handle()方法) - 权限检查
- 控制器/index 执行
Return Values
handle() 方法返回一个 Future<String?>:
- 返回
null——请求继续通过,进入下一个中间件或路由处理程序。 - 返回一个非空的
String——请求被阻止。中间件流水线停止,该路由不会被匹配。
如果一个路由附加了多个中间件,它们会按顺序依次执行。如果任何一个中间件返回了非空字符串,剩余的中间件会被跳过,请求也会被拒绝。
Creating a Middleware
要创建一个中间件,继承 Middleware 类并重写 handle() 方法:
import 'package:finch/finch_route.dart';
class MyMiddleware extends Middleware {
@override
Future<String?> handle() async {
// 在这里编写你的逻辑
return null; // 允许请求继续
}
}
Accessing the Request
在中间件内部,你可以通过 rq(由 Middleware 基类提供)访问当前请求:
class LogMiddleware extends Middleware {
@override
Future<String?> handle() async {
print('Request: ${rq.method} ${rq.uri.path}');
return null;
}
}
Adding Parameters
你可以在中间件中使用 rq.addParam() 向请求中注入数据。这些数据在下游的控制器和模板中都可以使用:
class TestMiddleware extends Middleware {
@override
Future<String?> handle() async {
rq.addParam('middleware', 'Test Middleware Active');
return null;
}
}
Blocking a Request
返回一个非空字符串即可阻止请求到达路由处理程序:
class ApiKeyMiddleware extends Middleware {
@override
Future<String?> handle() async {
// rq.headers 返回 Dart 的 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; // 允许请求继续
}
}
Attaching Middleware to Routes
有两种方式可以将中间件附加到路由上。
1. Using the Constructor
将中间件实例的列表传给 FinchRoute 的 middlewares 参数:
final testMiddleware = TestMiddleware();
final logMiddleware = LogMiddleware();
FinchRoute(
path: '/info',
index: homeController.info,
middlewares: [testMiddleware, logMiddleware],
);
2. Using the Fluent API
使用 .middleware() 方法以链式调用的方式将中间件挂载到路由上:
FinchRoute(
path: '/info',
index: homeController.info,
).middleware(TestMiddleware()).middleware(LogMiddleware());
Middleware on Parent Routes
当中间件被附加到一个拥有 children 的父路由上时,该中间件会在任何子路由被处理之前运行。这使你可以为一整组路由应用共享逻辑(例如身份验证或日志记录):
FinchRoute(
path: '/admin',
middlewares: [AdminMiddleware()],
children: [
FinchRoute(
path: '/dashboard',
index: adminController.dashboard,
),
FinchRoute(
path: '/users',
index: adminController.users,
),
],
);
在此示例中,AdminMiddleware 会在任何对 /admin/dashboard 或 /admin/users 的请求之前执行。
Practical Examples
Rate Limiting Middleware
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;
}
}
CORS Middleware
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;
}
}
Maintenance Mode Middleware
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 vs Auth Controller
| 特性 | Middleware | Auth Controller |
|---|---|---|
| 用途 | 通用的请求过滤 | 身份验证与授权 |
| 执行顺序 | 在 auth 之后、权限检查之前 | 在中间件之前 |
| 返回类型 | Future<String?> |
Future<bool> |
| 能否修改请求 | 可以(rq.addParam) |
可以 |
| 能否渲染错误 | 可以 | 可以 |
| 每个路由可挂载多个 | 可以(列表) | 每个路由一个 |
| 是否被子路由继承 | 是 | 是 |
当你需要完整的身份验证和 session 管理时,使用 Auth Controller。至于其他所有情况——日志记录、请求头校验、速率限制、CORS、功能开关等——则使用 Middleware。
Full Example
下面是在一个 Finch 应用中使用中间件的完整示例:
1. 创建中间件类:
// 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;
}
}
2. 在路由中注册中间件:
// 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],
),
];
3. 在控制器中访问中间件的数据:
class HomeController extends Controller {
Future<String> info() async {
final middlewareParam = rq.get('middleware');
return rq.renderString(text: 'Middleware says: $middlewareParam');
}
}
API Reference
Middleware (abstract class)
| 成员 | 类型 | 描述 |
|---|---|---|
rq |
Request(getter) |
访问当前的 HTTP 请求对象 |
handle() |
Future<String?> |
重写此方法以实现中间件逻辑。返回 null 表示放行,返回非空字符串表示阻止。 |
FinchRoute Middleware Members
| 成员 | 类型 | 描述 |
|---|---|---|
middlewares |
List<Middleware> |
附加到该路由上的中间件实例列表 |
middleware(m) |
FinchRoute |
用于添加中间件的链式方法;返回该路由本身以便继续链式调用 |
handleMiddlewares() |
Future<bool> |
按顺序执行所有中间件;如果全部通过则返回 true |