Skip to content

什么是控制器? 🤔 ​

控制器(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;
  }
}

最佳实践 ✨ ​

  1. 保持控制器简单
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);  // 业务逻辑放在服务层
  }
}
  1. 使用 DTO 和验证
typescript
export class CreateUserDto {
  @IsString()
  @IsNotEmpty()
  readonly name: string;

  @IsEmail()
  readonly email: string;
}
  1. 适当的错误处理
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);
  }
}

总结 📝 ​

通过这篇文章,我们学习了:

  1. 控制器的基本概念和用法
  2. 各种请求参数的获取方式
  3. 响应处理和状态码设置
  4. 异步处理方式
  5. 实际开发中的最佳实践

下一篇文章,我们将深入探讨 NestJS 的提供者(Provider)和依赖注入系统。

相关资源 📚 ​