第 169 章:控制器(Controller)详解
学习目标
- 掌握控制器装饰器
- 学会各种参数装饰器
- 理解请求/响应对象
- 学会子路由和分组
一、控制器基础
typescript
import { Controller, Get, Post, Put, Delete, Patch } from '@nestjs/common';
@Controller('users') // 路由前缀
export class UserController {
@Get() // GET /users
findAll() {}
@Get(':id') // GET /users/:id
findOne() {}
@Post() // POST /users
create() {}
@Put(':id') // PUT /users/:id
update() {}
@Patch(':id') // PATCH /users/:id
partialUpdate() {}
@Delete(':id') // DELETE /users/:id
remove() {}
}二、参数装饰器
2.1 路由参数
typescript
@Get(':id')
findOne(@Param('id') id: string) {
return { id };
}
// 多个参数
@Get(':category/:id')
find(@Param() params: { category: string; id: string }) {
return params;
}2.2 查询参数
typescript
@Get()
findAll(
@Query('page') page = 1,
@Query('size') size = 10,
@Query('sort') sort?: string,
) {
return { page, size, sort };
}2.3 请求体
typescript
interface CreateUserDto {
name: string;
email: string;
}
@Post()
create(@Body() body: CreateUserDto) {
return body;
}
// 部分字段
@Post()
create(@Body('name') name: string) {
return { name };
}2.4 Headers
typescript
@Get()
findAll(@Headers('authorization') auth: string) {
return { auth };
}2.5 Cookies
typescript
@Get()
findAll(@Cookies('sessionId') sessionId: string) {
return { sessionId };
}三、请求与响应对象
typescript
import { Req, Res } from '@nestjs/common';
import { Request, Response } from 'express';
@Get()
findAll(@Req() req: Request, @Res() res: Response) {
res.status(200).json({ code: 200, data: [] });
}四、状态码与 Headers
typescript
import { HttpCode, Header, HttpStatus } from '@nestjs/common';
@Post()
@HttpCode(HttpStatus.CREATED) // 201
create() {}
@Get()
@Header('Cache-Control', 'no-store')
findAll() {}
// 动态
@Post()
create(@Res({ passthrough: true }) res: Response) {
res.status(201);
return { ok: true };
}五、子路由
typescript
@Controller('users')
export class UserController {
@Get()
findAll() {}
@Get(':id')
findOne() {}
// 嵌套子资源
@Get(':id/posts')
findPosts(@Param('id') userId: string) {}
}六、动态路由匹配
typescript
// 数字 ID
@Get(':id(\\d+)')
findById(@Param('id') id: number) {} // 只匹配数字
// 邮箱格式
@Get(':email([^\\s@]+@[^\\s@]+\\.[^\\s@]+)')
findByEmail(@Param('email') email: string) {}七、异步控制器
typescript
@Get()
async findAll() {
const users = await this.userService.findAll();
return users;
}
// 完整异步
@Get()
async findAll(@Res({ passthrough: true }) res: Response) {
const users = await this.userService.findAll();
res.header('X-Total-Count', String(users.length));
return users;
}八、完整 CRUD 示例
typescript
import { Controller, Get, Post, Put, Delete, Param, Body, NotFoundException, ParseIntPipe } from '@nestjs/common';
interface User { id: number; name: string; email: string; }
interface CreateUserDto { name: string; email: string; }
interface UpdateUserDto { name?: string; email?: string; }
@Controller('api/users')
export class UserController {
private users: User[] = [];
private nextId = 1;
@Get()
findAll() {
return { code: 200, data: this.users };
}
@Get(':id')
findOne(@Param('id', ParseIntPipe) id: number) {
const user = this.users.find(u => u.id === id);
if (!user) throw new NotFoundException(`User ${id} not found`);
return { code: 200, data: user };
}
@Post()
create(@Body() dto: CreateUserDto) {
const user: User = { id: this.nextId++, ...dto };
this.users.push(user);
return { code: 201, data: user };
}
@Put(':id')
update(@Param('id', ParseIntPipe) id: number, @Body() dto: UpdateUserDto) {
const user = this.users.find(u => u.id === id);
if (!user) throw new NotFoundException();
Object.assign(user, dto);
return { code: 200, data: user };
}
@Delete(':id')
remove(@Param('id', ParseIntPipe) id: number) {
const idx = this.users.findIndex(u => u.id === id);
if (idx < 0) throw new NotFoundException();
this.users.splice(idx, 1);
return { code: 204 };
}九、ParseIntPipe(自动转换)
typescript
@Get(':id')
findOne(@Param('id', ParseIntPipe) id: number) {
// id 自动转为 number,非数字直接 400
}内置管道:
ValidationPipeParseIntPipeParseFloatPipeParseBoolPipeParseArrayPipeParseUUIDPipeParseEnumPipeDefaultValuePipe
十、本章小结
| 装饰器 | 作用 |
|---|---|
@Controller(path) | 控制器 |
@Get / @Post / @Put / @Delete | HTTP 方法 |
@Param(key) | 路径参数 |
@Query(key) | 查询参数 |
@Body() | 请求体 |
@Headers() | 请求头 |
@Req() @Res() | 原始对象 |
@HttpCode() | 状态码 |
@Header() | 响应头 |
动手练习
- 实现完整 CRUD 的 users 控制器
- 用 ParseIntPipe 转换 ID
- 给所有接口加统一返回格式
- 写一个带 query 分页的列表接口
推荐阅读
- 📖 Controllers
- 📖 路由参数
下一章:第 170 章:模块(Module)与依赖注入 →