1. 前言2. 快速生成 menus 资源3. 控制器3.1 依赖注入3.2 @Post()3.3 @Get()3.4 @Get(':id')3.5 @Patch(':id')3.6 @Delete(':id')3.7 @Query()4. 总结
1. 前言
同福客栈应该有一个菜单供客人点餐,每一道菜都有自己的信息(比如配料是什么、价格多少),配料和价格可以调整,可以不提供这道菜。
简而言之,菜单就是一个资源,RESTful 风格的 API 接口如下:
- GET /menus 获取所有菜品
- GET /menus/:id 获取某个菜品详情
- POST /menus 添加新的菜品
- PATCH /menus/:id 修改菜品详情
- DELETE /menus/:id 删除某个菜品
2. 快速生成 menus 资源
通过以下命令快速生成一个有关 menus 资源的控制器文件:
执行后会在 src 目录下生成一个有关 menus 资源的文件夹:

可以看到这组资源的构成与外层的 app 是类似的,都由 controller、service、module 文件构成。
先前说过“约定大于配置”,因此可以预见的是 controller 文件里应该写了 menus 相关的路由匹配以及对应执行的 service 方法,而 service 文件中包含了业务处理逻辑和具体响应给前端的内容,module 则负责如何组织它们。
3. 控制器
打开 menus.controller.ts 文件可以看到已经自动生成了相关代码,整体结构如下:
在 Nest 中使用了大量的装饰器语法,可以理解为“武器赋魔”,使之具备某种能力。
@Controller 用于将类 MenusController 标记为控制器,控制器是用于处理传入的 HTTP 请求的类。它定义了应用程序的路由,并根据不同的 HTTP 方法(如 GET、POST、PUT、DELETE 等)来处理请求。@Controller('menus') 中的字符串参数 'menus' 指定了控制器处理的基础路由路径,在这个例子中,所有与这个控制器相关的路由都会以 /menus 开头。3.1 依赖注入
在控制器类 MenusController 中有如下代码:
在 Nest 中有两个很重要的概念:
- 控制反转(Inversion of Control,简称 IOC)
- 依赖注入(Dependency Injection,简称 DI)
constructor(private readonly menusService: MenusService) {} :调用构造函数时,Nest 会通过 IOC 容器来自动实例化依赖项(这里指的是 MenusService:一个服务类,负责处理与菜单相关的业务逻辑),并自动将 MenusService 的实例注入到这个控制器类 MenusController 中。这样一来,就能在控制类
MenusController 中通过 this.menusService 来调用 MenusService 中的实例方法了。⚠️ 注意:通过
private readonly 声明,这个依赖只能在类内部使用,并且不能被修改。依赖注入这种解耦设计不再需要将繁杂的业务逻辑写在控制器中,而是将依赖关系从内部转移到外部,这意味着可以根据需求自由添加或更换依赖项。
在这里可以说
MenusController 依赖 MenusService,MenusService 为 MenusController 的依赖项。除了能够匹配到路由,下一步是要处理对应的 HTTP 请求(POST、GET、PATCH、DELETE)。
3.2 @Post()
@Post():这个装饰器标识此方法处理 POST 请求。通常用于创建新的资源。
create(@Body() createMenuDto: CreateMenuDto):这个方法接收一个createMenuDto参数,表示请求体中的数据。@Body()装饰器从 HTTP 请求的 body 中提取数据,并将其转换为CreateMenuDto对象。从这里可以看出:类中的方法是可以使用装饰器的,甚至是方法中的入参。
this.menusService.create(createMenuDto):调用MenusService实例的create方法,将createMenuDto传递给它。MenusService将负责处理实际的创建逻辑。
有了这段代码,掌柜的可以添加新的菜品了:

3.3 @Get()
@Get():这个装饰器标识此方法处理 GET 请求。通常用于获取资源的列表。
findAll():直接调用MenusService的findAll方法来获取所有菜单。
有了这段代码,客人们可以查看所有的菜品了:

3.4 @Get(':id')
@Get(':id'):这个装饰器标识此方法处理 GET 请求,并且路径中带有一个id参数。:id是一个路径参数,表示特定资源的标识符。
findOne(@Param('id') id: string):这个方法使用@Param('id')从请求路径中提取id参数,并将其转换为字符串类型传递给findOne方法。
this.menusService.findOne(+id):调用MenusService的findOne方法,传入的id参数前加上+表示将字符串id转换为数字。
有了这段代码,客人们可以查看某一道菜品的详情了:

3.5 @Patch(':id')
@Patch(':id'):这个装饰器标识此方法处理 PATCH 请求。PATCH 请求通常用于更新部分资源。
update(@Param('id') id: string, @Body() updateMenuDto: UpdateMenuDto):该方法从请求路径中提取id参数,从请求体中提取更新数据updateMenuDto。
this.menusService.update(+id, updateMenuDto):调用MenusService的update方法,更新指定id的菜单。
有了这段代码,掌柜的可以修改菜品信息了:

3.6 @Delete(':id')
@Delete(':id'):这个装饰器标识此方法处理 DELETE 请求,通常用于删除资源。
remove(@Param('id') id: string):从请求路径中提取id参数。
this.menusService.remove(+id):调用MenusService的remove方法,删除指定id的菜单。
有了这段代码,掌柜的可以删除菜品信息了:

至此,可以说前言部分的 API 接口都已经完成了。
3.7 @Query()
通过 3.3 的代码就可以查看所有的菜品,但是有时候希望通过名称 name 和类型 category 去过滤菜单,这就要用到查询字符串的装饰器
@Query ,假设用户可以通过以下接口拿到过滤菜单:- GET /menus/search?name=beef&category=Chinese
@Get('search') 装饰器将会匹配以 /menus/search 开头的路由。⚠️ 注意:放在动态路由前面,防止被优先捕获。
对应的 menus.service.ts 中的
search 方法修改如下:这样就可以分别从查询字符串中拿到
name 和 category 的值,并分别赋值给对应参数,再调用 MenusService 的 search 方法,找到指定 name 和 category 范围下的数据了。现在,客人可以筛选菜品了:


4. 总结
这篇博客介绍了如何快速生成一个资源,以及控制器相关的内容。结合装饰器,控制器类能够负责处理与菜单相关的 HTTP 请求。它利用 NestJS 提供的装饰器来标识每个方法处理的请求类型(GET、POST、PATCH、DELETE)以及路径参数。其他的各种装饰器将会在以后用到时才会出现。
业务逻辑部分通过依赖注入的方式委托给
MenusService 服务类来处理。这样使得控制器更加简洁,专注于路由处理和请求解析,而将具体的业务逻辑交由服务类负责。下一篇将讲解服务类的相关内容。- 作者:菠萝的蜜
- 链接:https://blog.miaoosi.com/backend/article/nestjs-the-mandatory-course-for-chefs-three-controllers
- 声明:本文采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。













