什么是控制器? 🤔
控制器(Controller)是 NestJS 应用程序的核心组件之一,负责处理传入的请求并向客户端返回响应。可以把它理解为应用程序的"交通警察" 🚥,负责指挥请求该往哪里走。
Express vs NestJS 对比 🆚
让我们先看看在 Express 和 NestJS 中如何处理 HTTP 请求:
Express 写法:
javascript
const express = require('express');
const router = express.Router();
router.get('/users', (req, res) => {
const users = [/* 用户数据 */];
res.json(users);
});
router.get('/users/:id', (req, res) => {
const { id } = req.params;
const user = /* 根据id查找用户 */;
res.json(user);
});
module.exports = router;NestJS 写法:
typescript
@Controller('users')
export class UsersController {
@Get()
findAll() {
return [/* 用户数据 */];
}
@Get(':id')
findOne(@Param('id') id: string) {
return /* 根据id查找用户 */;
}
}控制器装饰器详解 🎯
1. 路由装饰器
typescript
@Controller('users') // 👉 基础路径前缀
export class UsersController {
@Get() // GET /users
@Post() // POST /users
@Put(':id') // PUT /users/:id
@Delete(':id') // DELETE /users/:id
@Patch(':id') // PATCH /users/:id
@Options() // OPTIONS /users
@Head() // HEAD /users
@All() // 处理所有 HTTP 方法
}2. 路由通配符 🌟
typescript
@Controller('users')
export class UsersController {
@Get('profile*') // 匹配 /users/profile123, /users/profileabc 等
getProfile() {}
@Get('data?') // 匹配 /users/data 和 /users/datas
getData() {}
}请求参数获取 📝
1. 路径参数
typescript
@Get(':id')
findOne(@Param('id') id: string) {
return `用户ID: ${id}`;
}
// 获取所有参数
@Get(':id/:type')
findWithType(@Param() params: { id: string; type: string }) {
return `用户ID: ${params.id}, 类型: ${params.type}`;
}2. 查询参数 🔍
typescript
@Get()
find(
@Query('page') page: number = 1,
@Query('limit') limit: number = 10
) {
return `第 ${page} 页,每页 ${limit} 条`;
}3. 请求体 📦
typescript
@Post()
create(@Body() createUserDto: CreateUserDto) {
return `创建用户: ${createUserDto.name}`;
}
// 获取特定字段
@Post()
createWithField(@Body('name') name: string) {
return `创建用户: ${name}`;
}4. 请求头和Cookie 🍪
typescript
@Get()
findAll(
@Headers('user-agent') userAgent: string,
@Headers() headers: any,
@Cookies('auth') auth: string
) {
return `User Agent: ${userAgent}`;
}响应处理 💫
1. 状态码装饰器
typescript
@Post()
@HttpCode(HttpStatus.CREATED) // 👉 设置状态码 201
create(@Body() createUserDto: CreateUserDto) {
return '用户创建成功';
}2. 响应头装饰器
typescript
@Get()
@Header('Cache-Control', 'none') // 👉 设置响应头
findAll() {
return '所有用户';
}3. 重定向
typescript
@Get('old-path')
@Redirect('new-path', 301) // 👉 重定向到新路径
oldPath() {}异步处理 ⚡
NestJS 完全支持异步函数:
typescript
@Controller('users')
export class UsersController {
constructor(private usersService: UsersService) {}
@Get()
async findAll(): Promise<User[]> {
return await this.usersService.findAll();
}
// 也支持 Observable
@Get(':id')
findOne(@Param('id') id: string): Observable<User> {
return this.usersService.findOne(id);
}
}子域路由 🌐
typescript
@Controller({ host: 'api.example.com' }) // 👉 子域名路由
export class ApiController {
@Get()
index() {
return 'API 首页';
}
}实战示例 💡
让我们创建一个完整的用户控制器:
typescript
@Controller('users')
export class UsersController {
constructor(private readonly usersService: UsersService) {}
@Post()
@HttpCode(HttpStatus.CREATED)
@UsePipes(ValidationPipe) // 👉 使用验证管道
async create(@Body() createUserDto: CreateUserDto) {
try {
return await this.usersService.create(createUserDto);
} catch (error) {
throw new HttpException('创建失败', HttpStatus.BAD_REQUEST);
}
}
@Get()
@UseGuards(AuthGuard) // 👉 使用认证守卫
async findAll(
@Query('page') page: number = 1,
@Query('limit') limit: number = 10,
@User() user: UserEntity // 👉 自定义装饰器
) {
return await this.usersService.findAll({
page,
limit,
userId: user.id
});
}
@Get(':id')
@UseInterceptors(CacheInterceptor) // 👉 使用缓存拦截器
async findOne(@Param('id') id: string) {
const user = await this.usersService.findOne(id);
if (!user) {
throw new NotFoundException('用户不存在');
}
return user;
}
}最佳实践 ✨
- 保持控制器简单
typescript
// ❌ 不好的做法
@Controller('users')
export class UsersController {
@Post()
async create(@Body() dto: CreateUserDto) {
// 大量的业务逻辑处理
}
}
// ✅ 好的做法
@Controller('users')
export class UsersController {
constructor(private readonly usersService: UsersService) {}
@Post()
async create(@Body() dto: CreateUserDto) {
return this.usersService.create(dto); // 业务逻辑放在服务层
}
}- 使用 DTO 和验证
typescript
export class CreateUserDto {
@IsString()
@IsNotEmpty()
readonly name: string;
@IsEmail()
readonly email: string;
}- 适当的错误处理
typescript
@Get(':id')
async findOne(@Param('id') id: string) {
try {
const user = await this.usersService.findOne(id);
if (!user) {
throw new NotFoundException('用户不存在');
}
return user;
} catch (error) {
// 根据错误类型抛出适当的异常
throw new HttpException(error.message, HttpStatus.INTERNAL_SERVER_ERROR);
}
}总结 📝
通过这篇文章,我们学习了:
- 控制器的基本概念和用法
- 各种请求参数的获取方式
- 响应处理和状态码设置
- 异步处理方式
- 实际开发中的最佳实践
下一篇文章,我们将深入探讨 NestJS 的提供者(Provider)和依赖注入系统。