中间件
中间件在请求到达控制器之前在管道中处理请求。它们处理横切关注点,如身份验证、日志记录、验证和请求转换。
概述
YasuiJS 使用带有 @Middleware() 装饰器的基于类的中间件。中间件基于 Web 标准构建,可在所有支持的运行时(Node.js、Deno、Bun)中工作。
重要提示:YasuiJS 4.x 使用 Web 标准 Request/Response 而不是 Express。Express 风格的中间件(如 cors、helmet 等)不兼容。请使用与 Web 标准兼容的替代方案或编写原生 YasuiJS 中间件。
中间件可以在三个级别应用,具有不同的执行优先级:
- 应用程序级别 - 应用于所有请求
- 控制器级别 - 应用于控制器中的所有路由
- 端点级别 - 应用于特定路由
typescript
import { Middleware } from 'yasui';
@Middleware()
export class LoggingMiddleware {
use() {
console.log('Request received');
}
}基于类的中间件
中间件装饰器
@Middleware() 装饰器将类标记为中间件。该类必须实现 use() 方法。您可以选择实现 YasuiJS 提供的 IMiddleware 接口来强制执行方法签名。
typescript
import { Middleware, IMiddleware, Request, Req } from 'yasui';
@Middleware()
export class AuthMiddleware implements IMiddleware {
use(@Req() req: Request) {
const token = req.headers.authorization;
if (!token) {
throw new HttpError(401, 'Unauthorized');
}
// 在此处验证令牌逻辑
// 如果返回 nothing/void,将继续到下一个中间件或控制器
}
}注意: 中间件的工作方式类似于控制器方法 - 您可以返回值、抛出错误或不返回任何内容以继续执行。如果您需要手动控制执行流程,使用 @Next() 是可选的。
中间件中的参数装饰器
中间件可以使用与控制器相同的参数装饰器,并且也受益于自动错误捕获:
typescript
@Middleware()
export class ValidationMiddleware {
use(
@Body() body: any,
@Query('validate') shouldValidate: boolean,
@Header('content-type') contentType: string
) {
if (shouldValidate && !this.isValid(body)) {
throw new HttpError(400, 'Invalid request data');
}
}
private isValid(data: any): boolean {
// 验证逻辑
return true;
}
}自动类型转换: 中间件中的所有参数装饰器都受益于与控制器相同的自动类型转换。参数在中间件执行之前会转换为其指定的类型。
依赖注入
由于中间件类的行为类似于控制器,它们也允许以相同的方式进行依赖注入:
typescript
@Middleware()
export class LoggingMiddleware {
constructor (
private validationService: ValidationService, // 标准注入
@Inject('CONFIG') private config: AppConfig, // 预注册的自定义注入
) {}
use(
@Body() body: any,
@Inject() anotherService: AnotherService, // 在方法级别相同
) {
if (!this.validationService.isValid(body)) {
throw new HttpError(400, 'Invalid request data');
}
}
}编写自定义中间件
您可以为常见用例创建中间件。以下是两种模式:
模式 1:简单验证(不需要 @Next())
typescript
@Middleware()
export class ApiKeyMiddleware implements IMiddleware {
use(@Header('x-api-key') apiKey: string) {
if (!apiKey || apiKey !== 'expected-key') {
throw new HttpError(401, 'Invalid API key');
}
// 将自动继续
}
}模式 2:响应修改(使用 @Next())
当您需要修改响应时,使用 @Next():
typescript
@Middleware()
export class CorsMiddleware implements IMiddleware {
async use(@Req() req: Request, @Next() next: NextFunction) {
const response = await next();
// 向响应添加 CORS 头
const headers = new Headers(response.headers);
headers.set('Access-Control-Allow-Origin', '*');
headers.set('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, PATCH');
headers.set('Access-Control-Allow-Headers', 'Content-Type, Authorization');
return new Response(response.body, {
status: response.status,
statusText: response.statusText,
headers,
});
}
}中间件使用级别
应用程序级别
应用于整个应用程序的所有请求:
typescript
yasui.createServer({
controllers: [UserController],
middlewares: [LoggingMiddleware, SecurityMiddleware]
});控制器级别
应用于特定控制器内的所有路由:
typescript
// 单个中间件
@Controller('/api/users', AuthMiddleware)
export class UserController {
// 所有路由都需要身份验证
}
// 多个中间件
@Controller('/api/admin', AuthMiddleware, ValidationMiddleware)
export class AdminController {
// 所有路由都有身份验证 + 验证
}端点级别
仅应用于特定路由:
typescript
@Controller('/api/users')
export class UserController {
@Get('/')
getUsers() {
// 无中间件
}
@Post('/', ValidationMiddleware)
createUser() {
// 仅验证中间件
}
@Delete('/:id', AuthMiddleware, ValidationMiddleware)
deleteUser() {
// 身份验证和验证中间件
}
}执行顺序
中间件按以下顺序执行:
- 应用程序中间件(按注册顺序)
- 控制器中间件(按声明顺序)
- 端点中间件(按声明顺序)
- 控制器方法
typescript
// 执行顺序示例:
yasui.createServer({
middlewares: [GlobalMiddleware] // 1. 第一个
});
@Controller('/users', ControllerMiddleware) // 2. 第二个
export class UserController {
@Post('/', EndpointMiddleware) // 3. 第三个
createUser() {
// 4. 最后是控制器方法
}
}