3. 控制器与路由机制:@Controller 装饰器、路由前缀、HTTP 方法装饰器、参数装饰器
在 NestJS 中,控制器(Controller) 是处理传入 HTTP 请求并返回响应的核心组件。它通过装饰器模式将类与方法映射到具体的路由路径和 HTTP 方法上,是构建 RESTful API 的基石。本章将深入剖析 @Controller、路由前缀、HTTP 方法装饰器以及参数装饰器的使用方式与底层原理。
3.1 @Controller 装饰器与路由前缀
@Controller() 装饰器用于标记一个类为 NestJS 控制器。它接受一个可选参数——路由前缀(Route Prefix),用于为该控制器内的所有路由方法设置公共路径前缀。
核心作用:路由前缀不仅用于组织代码结构(如按模块分组),还能避免路由冲突,并简化版本控制(如
/api/v1/users)。
| 装饰器 | 参数 | 说明 | 示例 |
|---|---|---|---|
@Controller() |
无 | 控制器无全局前缀,路由完全由方法装饰器定义 | @Controller() |
@Controller('users') |
字符串 | 所有路由方法前自动添加 /users 前缀 |
@Controller('users') |
@Controller('api/v1/users') |
路径字符串 | 支持多级路径前缀,常用于 API 版本管理 | @Controller('api/v1/users') |
import { Controller, Get } from '@nestjs/common';
@Controller('users') // 路由前缀:/users
export class UsersController {
@Get('profile') // 完整路由:GET /users/profile
getProfile() {
return { userId: 1, name: 'Alice' };
}
}
3.2 HTTP 方法装饰器
NestJS 为标准的 HTTP 方法提供了对应的装饰器,用于将控制器方法绑定到特定的 HTTP 请求类型和路由路径上。这些装饰器均接受一个路径字符串参数,用于定义相对于控制器前缀的子路径。
@Get():处理 GET 请求,用于获取资源。@Post():处理 POST 请求,用于创建新资源。@Put():处理 PUT 请求,用于完整更新资源。@Patch():处理 PATCH 请求,用于部分更新资源。@Delete():处理 DELETE 请求,用于删除资源。@Options():处理 OPTIONS 请求,用于预检请求。@Head():处理 HEAD 请求,仅获取响应头。@All():处理所有 HTTP 方法。
最佳实践:路径参数支持 通配符(如
@Get('ab*cd') 匹配 /abXcd)和 路径参数占位符(如 @Get(':id')),后者需配合 @Param() 装饰器使用。
import { Controller, Get, Post, Put, Delete, Body, Param } from '@nestjs/common';
@Controller('users')
export class UsersController {
@Get() // GET /users
findAll() { /* ... */ }
@Get(':id') // GET /users/123
findOne(@Param('id') id: string) { /* ... */ }
@Post() // POST /users
create(@Body() data: any) { /* ... */ }
@Put(':id') // PUT /users/123
update(@Param('id') id: string, @Body() data: any) { /* ... */ }
@Delete(':id') // DELETE /users/123
remove(@Param('id') id: string) { /* ... */ }
}
3.3 参数装饰器
参数装饰器用于从传入的请求对象中提取特定数据,并将其注入到控制器方法的参数中。NestJS 提供了丰富的内置参数装饰器,覆盖了常见的请求数据源。
| 装饰器 | 提取的数据 | 常用场景 |
|---|---|---|
@Request() / @Req() |
完整的 Express/Fastify 请求对象 | 访问底层请求对象(如原始 headers) |
@Response() / @Res() |
完整的 Express/Fastify 响应对象 | 直接控制响应(如流式传输) |
@Param(key?: string) |
路由参数(如 :id) |
获取 URL 路径中的动态段 |
@Body(key?: string) |
请求体(JSON / 表单) | 创建或更新资源时获取提交数据 |
@Query(key?: string) |
查询参数(如 ?page=1) |
分页、过滤、搜索参数 |
@Headers(name?: string) |
请求头 | 获取认证令牌、自定义头 |
@Ip() |
客户端 IP 地址 | 日志记录、访问控制 |
@Session() |
会话对象 | 用户会话管理 |
注意:当使用
@Res() 装饰器时,NestJS 会进入 底层响应模式,此时需手动调用 response.send() 等方法,否则路由将挂起。建议仅在需要流式响应或自定义响应头时使用。
import { Controller, Get, Post, Param, Body, Query, Headers, Ip } from '@nestjs/common';
@Controller('orders')
export class OrdersController {
@Get()
findAll(
@Query('page') page: number = 1,
@Query('limit') limit: number = 10,
@Headers('authorization') auth: string,
@Ip() clientIp: string,
) {
console.log(`请求来自 IP: ${clientIp}`);
return { page, limit, auth };
}
@Post()
create(@Body() orderData: any) {
// 创建订单逻辑
return { success: true, data: orderData };
}
@Get(':orderId')
findOne(@Param('orderId') orderId: string) {
return { orderId };
}
}
3.4 路由匹配优先级与通配符
NestJS 的路由匹配遵循 先注册先匹配 的原则,但更具体的路由(如静态路径)会优先于动态路径(如 :id)被匹配。此外,路径支持通配符模式。
- 静态路径优先:
@Get('profile')优先于@Get(':id')。 - 通配符
*:匹配任意字符序列,如@Get('ab*cd')匹配/abXcd、/ab123cd。 - 通配符
?:匹配单个字符(部分底层平台支持)。 - 通配符
+:匹配一个或多个字符(部分底层平台支持)。
调试技巧:在开发阶段,可以通过
NestFactory.create(AppModule, { logger: ['debug'] }) 启用调试日志,查看 NestJS 注册的所有路由及其顺序。
3.5 自定义装饰器
当内置装饰器无法满足需求时,NestJS 允许通过 createParamDecorator 创建自定义参数装饰器,实现数据提取逻辑的复用。
import { createParamDecorator, ExecutionContext } from '@nestjs/common';
export const User = createParamDecorator(
(data: string, ctx: ExecutionContext) => {
const request = ctx.switchToHttp().getRequest();
const user = request.user; // 假设认证中间件已将用户信息挂载到 request
return data ? user?.[data] : user;
},
);
// 在控制器中使用
@Get('profile')
getProfile(@User('name') userName: string) {
return { userName };
}
进阶用法:自定义装饰器可以结合 管道(Pipe) 进行数据验证与转换,实现声明式的参数处理。例如
@User(new ValidationPipe())。
3.6 小结
控制器与路由机制是 NestJS 应用的门面层。通过 @Controller 定义模块级路由前缀,结合 HTTP 方法装饰器与参数装饰器,可以快速构建出结构清晰、类型安全的 API 端点。理解路由匹配优先级与参数提取流程,是后续实现中间件、守卫、拦截器等高级功能的基础。