Middleware

中间件提供了一种在 HTTP 请求到达路由处理程序之前对其进行检查和过滤的机制。

What is Middleware?

Finch 中的中间件是一个位于传入 HTTP 请求与你的路由处理程序(控制器或 index 函数)之间的类。它允许你在请求到达控制器之前运行逻辑——例如检查请求头、记录请求日志、校验令牌、修改请求参数,或阻止未授权的访问。

每个中间件类都必须继承抽象类 Middleware 并实现 handle() 方法。

How Middleware Works

当一个请求匹配到附加了中间件的路由时,Finch 会在将请求传递给控制器或 index 函数之前,按顺序执行中间件流水线。整体流程如下:

  1. 路由模式匹配
  2. HTTP 方法、主机和端口校验
  3. 身份验证检查(auth
  4. 中间件执行(依次运行每个中间件的 handle() 方法)
  5. 权限检查
  6. 控制器/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

将中间件实例的列表传给 FinchRoutemiddlewares 参数:

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