NestJS控制器 (Controllers)

学习目标

  • 理解控制器在NestJS中的作用和地位
  • 掌握路由定义和HTTP方法的使用
  • 学会处理各种类型的请求参数
  • 能够创建基本的API端点

核心知识点

1. 控制器概念

控制器是NestJS应用的核心组件之一,负责处理传入的HTTP请求并返回响应。它们的主要职责是:

  • 接收客户端请求
  • 处理请求数据
  • 调用相应的服务进行业务逻辑处理
  • 返回处理结果给客户端

在NestJS中,控制器通过装饰器(decorators)来定义路由和HTTP方法。

2. 基本控制器创建

创建一个基本控制器的步骤:

  1. 使用@Controller()装饰器标记一个类
  2. 在类中定义处理不同HTTP请求的方法
  3. 使用HTTP方法装饰器(如@Get()@Post()等)标记这些方法
import { Controller, Get } from '@nestjs/common';

@Controller('cats')
export class CatsController {
  @Get()
  findAll() {
    return 'This action returns all cats';
  }
}

3. 路由定义

基本路由

控制器的基本路由由@Controller()装饰器指定:

@Controller('users') // 基础路由为 /users
export class UsersController {
  // ...
}

方法级路由

方法级路由由HTTP方法装饰器指定,可以是相对于控制器基础路由的路径:

@Controller('users')
export class UsersController {
  @Get() // 完整路由: /users
  findAll() { /* ... */ }
  
  @Get('profile') // 完整路由: /users/profile
  getProfile() { /* ... */ }
}

4. HTTP方法

NestJS提供了多种HTTP方法装饰器:

  • @Get() - 处理GET请求
  • @Post() - 处理POST请求
  • @Put() - 处理PUT请求
  • @Delete() - 处理DELETE请求
  • @Patch() - 处理PATCH请求
  • @Options() - 处理OPTIONS请求
  • @Head() - 处理HEAD请求
  • @All() - 处理所有HTTP方法的请求

5. 请求参数处理

路由参数

使用@Param()装饰器获取路由参数:

@Controller('users')
export class UsersController {
  @Get(':id')
  findOne(@Param('id') id: string) {
    return `This action returns a user with id: ${id}`;
  }
}

查询参数

使用@Query()装饰器获取查询参数:

@Controller('users')
export class UsersController {
  @Get()
  findAll(@Query('limit') limit: number, @Query('offset') offset: number) {
    return `This action returns users with limit: ${limit} and offset: ${offset}`;
  }
}

请求体

使用@Body()装饰器获取请求体:

@Controller('users')
export class UsersController {
  @Post()
  create(@Body() user: CreateUserDto) {
    return `This action creates a user: ${user.name}`;
  }
}

请求头

使用@Headers()装饰器获取请求头:

@Controller('users')
export class UsersController {
  @Get()
  findAll(@Headers('Authorization') authorization: string) {
    return `Authorization header: ${authorization}`;
  }
}

使用@Cookie()装饰器获取Cookie:

@Controller('users')
export class UsersController {
  @Get()
  findAll(@Cookie('name') name: string) {
    return `Cookie name: ${name}`;
  }
}

6. 响应处理

基本响应

控制器方法可以直接返回各种类型的值,NestJS会自动处理响应:

@Get()
findAll() {
  return ['cat1', 'cat2', 'cat3']; // 会自动序列化为JSON
}

状态码

使用@HttpCode()装饰器设置响应状态码:

@Post()
@HttpCode(201)
create() {
  return 'This action creates a cat';
}

响应头

使用@Header()装饰器设置响应头:

@Post()
@Header('Cache-Control', 'none')
create() {
  return 'This action creates a cat';
}

重定向

使用@Redirect()装饰器设置重定向:

@Get()
@Redirect('https://nestjs.com', 301)
redirect() {
  // 可选的重定向逻辑
}

7. 路由通配符

NestJS支持路由通配符:

@Controller('files')
export class FilesController {
  @Get('ab*cd')
  findAll() {
    return 'This route uses a wildcard';
  }
}

8. 异步控制器方法

控制器方法可以是异步的,返回Promise或Observable:

@Get()
async findAll() {
  return await this.catsService.findAll();
}

@Get()
findAll(): Observable<any[]> {
  return this.catsService.findAll();
}

实践案例分析

案例:创建一个用户管理API

需求分析

我们需要创建一个简单的用户管理API,支持以下功能:

  • 获取所有用户
  • 获取单个用户
  • 创建新用户
  • 更新用户信息
  • 删除用户

实现步骤

  1. 创建用户控制器
  2. 定义路由和HTTP方法
  3. 实现请求参数处理
  4. 添加响应处理

代码实现

import {
  Controller,
  Get,
  Post,
  Put,
  Delete,
  Param,
  Body,
  Query,
  HttpCode,
  HttpStatus,
} from '@nestjs/common';

// 简单的DTO类
class CreateUserDto {
  name: string;
  email: string;
}

class UpdateUserDto {
  name?: string;
  email?: string;
}

@Controller('users')
export class UsersController {
  // 模拟用户数据
  private users = [
    { id: '1', name: '张三', email: 'zhangsan@example.com' },
    { id: '2', name: '李四', email: 'lisi@example.com' },
  ];

  @Get()
  findAll(@Query('limit') limit?: number) {
    if (limit) {
      return this.users.slice(0, limit);
    }
    return this.users;
  }

  @Get(':id')
  findOne(@Param('id') id: string) {
    const user = this.users.find(u => u.id === id);
    if (!user) {
      return { status: 404, message: 'User not found' };
    }
    return user;
  }

  @Post()
  @HttpCode(HttpStatus.CREATED)
  create(@Body() createUserDto: CreateUserDto) {
    const newUser = {
      id: (this.users.length + 1).toString(),
      ...createUserDto,
    };
    this.users.push(newUser);
    return newUser;
  }

  @Put(':id')
  update(@Param('id') id: string, @Body() updateUserDto: UpdateUserDto) {
    const index = this.users.findIndex(u => u.id === id);
    if (index === -1) {
      return { status: 404, message: 'User not found' };
    }
    
    this.users[index] = {
      ...this.users[index],
      ...updateUserDto,
    };
    
    return this.users[index];
  }

  @Delete(':id')
  @HttpCode(HttpStatus.NO_CONTENT)
  remove(@Param('id') id: string) {
    const index = this.users.findIndex(u => u.id === id);
    if (index === -1) {
      return { status: 404, message: 'User not found' };
    }
    
    this.users.splice(index, 1);
    return;
  }
}

代码解析

  1. 路由定义:使用@Controller(&#39;users&#39;)定义基础路由为/users
  2. HTTP方法:使用@Get(), @Post(), @Put(), @Delete()装饰器定义不同的HTTP方法处理
  3. 参数处理
    • 使用@Param()获取路由参数
    • 使用@Body()获取请求体
    • 使用@Query()获取查询参数
  4. 响应处理
    • 使用@HttpCode()设置响应状态码
    • 直接返回数据,NestJS会自动序列化

测试结果

请求方法 请求路径 描述 预期响应
GET /users 获取所有用户 [{&quot;id&quot;:&quot;1&quot;,&quot;name&quot;:&quot;张三&quot;,&quot;email&quot;:&quot;zhangsan@example.com&quot;},{&quot;id&quot;:&quot;2&quot;,&quot;name&quot;:&quot;李四&quot;,&quot;email&quot;:&quot;lisi@example.com&quot;}]
GET /users?limit=1 获取1个用户 [{&quot;id&quot;:&quot;1&quot;,&quot;name&quot;:&quot;张三&quot;,&quot;email&quot;:&quot;zhangsan@example.com&quot;}]
GET /users/1 获取ID为1的用户 {&quot;id&quot;:&quot;1&quot;,&quot;name&quot;:&quot;张三&quot;,&quot;email&quot;:&quot;zhangsan@example.com&quot;}
POST /users 创建新用户 {&quot;id&quot;:&quot;3&quot;,&quot;name&quot;:&quot;王五&quot;,&quot;email&quot;:&quot;wangwu@example.com&quot;} (状态码: 201)
PUT /users/1 更新用户信息 {&quot;id&quot;:&quot;1&quot;,&quot;name&quot;:&quot;张三更新&quot;,&quot;email&quot;:&quot;zhangsan@example.com&quot;}
DELETE /users/1 删除用户 空响应 (状态码: 204)

互动思考问题

  1. 思考:控制器和服务的职责有什么不同?在实际应用中如何划分它们的边界?

  2. 讨论:在处理复杂的业务逻辑时,控制器应该直接实现业务逻辑还是调用服务层?为什么?

  3. 实践:尝试创建一个博客API,支持文章的CRUD操作,包括获取所有文章、获取单篇文章、创建文章、更新文章和删除文章。

  4. 挑战:如何处理控制器中的错误?例如,当用户请求不存在的资源时,应该返回什么状态码和错误信息?

  5. 扩展:了解NestJS的路由守卫(Guards)和拦截器(Interceptors),思考它们如何与控制器配合使用来实现更复杂的功能。

小结

本集我们学习了NestJS控制器的核心概念和使用方法,包括:

  • 控制器的基本概念和作用
  • 路由定义和HTTP方法的使用
  • 各种类型的请求参数处理
  • 响应处理和状态码设置
  • 异步控制器方法的实现

通过实践案例,我们创建了一个完整的用户管理API,展示了控制器的实际应用。控制器是NestJS应用的入口点,它们接收和处理客户端请求,是构建API的基础组件。

在下一集中,我们将学习NestJS的提供者(Providers),了解NestJS的依赖注入系统,这将帮助我们更好地组织和管理应用的业务逻辑。

« 上一篇 安装与项目初始化 下一篇 » NestJS提供者 (Providers)