控制器是 NestJS 中负责接收 HTTP 请求的组件,而各种方法装饰器和参数装饰器则帮助我们精确地定义如何响应不同类型的请求,以及如何方便地从请求中提取所需的信息。这就像服务员接收顾客的点单,需要准确记录菜品名称(路由路径)、份数(请求参数)、特殊要求(查询参数)、配料调整(请求体)等各种细节。
控制器前缀 (@Controller('prefix')
):
在 @Controller()
装饰器中指定字符串参数,可以为控制器中的所有路由设置一个公共的前缀。
// src/users/users.controller.ts (修改)
import { Controller, Get } from '@nestjs/common';
// 控制器前缀设置为 'api/users'
@Controller('api/users')
export class UsersController {
// ... 构造函数和 getHello 等方法 ...
@Get() // 实际处理的路径是 /api/users
findAll(): string { /* ... */ return '所有用户'; }
@Get(':id') // 实际处理的路径是 /api/users/:id
findOne(): string { /* ... */ return '单个用户'; }
}
现在访问用户相关的接口就需要以 /api/users
开头了。这有助于组织 API 端点。
方法装饰器 (@Get
, @Post
, etc.):
这些装饰器用于标记控制器中的方法,告诉 NestJS 这个方法应该处理哪种 HTTP 方法以及对应的路由路径(相对于控制器前缀)。
// src/products/products.controller.ts (示例)
import { Controller, Get, Post, Put, Delete, Param, Body } from '@nestjs/common';
import { ProductsService } from './products.service';
@Controller('api/products')
export class ProductsController {
constructor(private readonly productsService: ProductsService) {}
@Get() // GET /api/products
findAll() {
return this.productsService.getProducts();
}
@Get(':id') // GET /api/products/:id
findOne(@Param('id') id: string) { // 使用 @Param('id') 获取路径参数 id
// 在实际应用中,可能需要将 id 从 string 转换为 number
return `获取产品 ID: ${id}`;
}
@Post() // POST /api/products
create(@Body() productData: any) { // 使用 @Body() 获取整个请求体
console.log('收到新产品数据:', productData);
// 在实际应用中,会调用 Service 添加产品到数据库
return { message: '产品创建成功', data: productData };
}
@Put(':id') // PUT /api/products/:id
update(@Param('id') id: string, @Body() productData: any) {
console.log(`更新产品 ID: ${id}, 数据:`, productData);
return { message: `产品 ID ${id} 更新成功`, data: productData };
}
@Delete(':id') // DELETE /api/products/:id
remove(@Param('id') id: string) {
console.log(`删除产品 ID: ${id}`);
return { message: `产品 ID ${id} 删除成功` };
}
// @All() 可以匹配所有 HTTP 方法
// @All()
// handleAll() {
// return '匹配所有方法';
// }
}
参数装饰器 (@Param
, @Query
, @Body
, @Headers
, @Req
, @Res
):
这些装饰器用于标记控制器方法的参数,告诉 NestJS 应该从请求对象中提取哪个部分的数据并作为该参数的值。
@Param()
: 提取路由参数。可以加上字符串参数指定提取哪个参数 (@Param('id')
),或者不加参数提取包含所有路由参数的对象 (@Param()
).@Query()
: 提取 URL 查询参数。可以加上字符串参数指定提取哪个参数 (@Query('keyword')
),或者不加参数提取包含所有查询参数的对象 (@Query()
).@Body()
: 提取请求体。可以加上字符串参数指定提取请求体中的某个属性 (@Body('name')
),或者不加参数提取整个请求体对象 (@Body()
). 注意: 使用@Body()
前,需要确保已经配置了请求体解析中间件(Express 内置的express.json()
和express.urlencoded()
,NestJS 默认集成了)。@Headers()
: 提取请求头。可以加上字符串参数指定提取某个特定的请求头 (@Headers('Content-Type')
),或者不加参数提取包含所有请求头的对象 (@Headers()
).@Req()
: 注入原始的 Express/Fastify 请求对象。@Res()
: 注入原始的 Express/Fastify 响应对象。注意: 除非需要直接控制响应流(比如发送文件),否则通常不推荐使用@Res()
并手动调用res.send()
,res.json()
等。NestJS 会自动处理返回值并发送响应,这样可以更好地利用 NestJS 的拦截器等特性。
小例子:处理各种请求参数
// src/example/example.controller.ts (假设你创建了一个 example 模块和控制器)
import { Controller, Get, Post, Query, Param, Body, Headers } from '@nestjs/common';
@Controller('api/example')
export class ExampleController {
@Get('greet') // GET /api/example/greet?name=Alice&greeting=Hello
greet(
@Query('name') name?: string, // 获取查询参数 'name',可选
@Query('greeting') greeting = 'Hi' // 获取查询参数 'greeting',提供默认值
) {
return `${greeting}, ${name || 'Guest'}!`;
}
@Get('items/:itemId/details') // GET /api/example/items/123/details
getItemDetails(@Param('itemId') itemId: string) {
// itemId 是从路径中提取的参数
return `获取项目 ID: ${itemId} 的详细信息`;
}
@Post('echo') // POST /api/example/echo
echo(@Body() data: any, @Headers('Content-Type') contentType: string) {
// data 是请求体,contentType 是请求头 'Content-Type' 的值
console.log('请求体:', data);
console.log('Content-Type:', contentType);
return { receivedData: data, contentType: contentType };
}
}
小结: NestJS 的控制器通过方法装饰器 (@Get
, @Post
等) 和参数装饰器 (@Param
, @Query
, @Body
等) 提供了一种声明式的方式来定义路由和提取请求信息。这使得请求处理逻辑清晰、易于阅读和维护,极大地简化了后端开发中处理请求参数的繁琐工作。
练习:
- 在你之前的
my-backend
项目中,修改products.controller.ts
文件,为控制器添加一个前缀,比如'api/products'
。 - 修改
findOne
方法,使用@Param('id')
参数装饰器获取产品 ID。假设 ID 是数字类型,尝试为方法参数添加类型注解id: string
。 - 为
ProductsController
添加一个 POST 方法create
(@Post()
),使用@Body()
参数装饰器获取整个请求体。在方法中打印出接收到的请求体。 - 为
ProductsController
添加 PUT (@Put(':id')
) 和 DELETE (@Delete(':id')
) 方法,分别获取路由参数 ID 和请求体(对于 PUT)。 - 运行应用,使用工具(如 Postman)测试你新添加的路由,发送带有参数或请求体的请求,并在服务器控制台查看输出。