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) { /* ... */ }
}
控制器与路由映射流程 控制器与路由映射流程 HTTP 请求 @Controller('users') 路由前缀:/users @Get(':id') 子路径:/:id 完整路由 GET /users/123 @Param('id') 提取路径参数 说明:NestJS 将控制器前缀与 HTTP 方法装饰器的子路径拼接,形成最终路由。 参数装饰器负责从请求中提取数据并注入到方法参数中。

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 注册的所有路由及其顺序。
参数装饰器数据流 参数装饰器数据流 HTTP 请求对象 包含所有请求数据 @Param() @Body() @Query() @Headers() 控制器方法参数 findAll( @Query('page') page, @Headers('auth') auth, @Ip() clientIp ) 说明:参数装饰器从请求对象中提取特定数据,并自动注入到控制器方法的对应参数中。 每个装饰器只关注自己负责的数据片段,实现关注点分离。

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 端点。理解路由匹配优先级与参数提取流程,是后续实现中间件、守卫、拦截器等高级功能的基础。