[NestJS 带你飞!] DAY03 - Controller (上)

在 Nest 的世界里,Controller 负责路由的配置并处理来自客户端的请求,而每一个 Controller 都可以依照需求来设计不同 Http Method 的资源,就好像外场服务生负责带位、协助客人点餐一样,并根据客户的需求做出相对应的回应:
https://ithelp.ithome.com.tw/upload/images/20210303/20119338DiMtfCY7e3.png

总体来说,Controller 就是一个处理客户端请求,并将相同性质的资源整合在一起的元件。

建置 Controller

所有的 Controller 都必须使用 @Controller 装饰器来定义。可以用 NestCLI 快速生成 Controller:

$ nest generate controller <CONTROLLER_NAME>

注意<CONTROLLER_NAME> 可以含有路径,如:features/todo,这样就会在 src 资料夹下建立该路径并含有 Controller。

这边我建立了一个名为 todo 的 Controller:

$ nest generate controller todo

src 底下会看见一个名为 todo 的资料夹,里面有 todo.controller.ts 以及 todo.controller.spec.ts
https://ithelp.ithome.com.tw/upload/images/20210303/20119338V5DCWGtyKY.png

由於只有建立 Controller,所以会自动将其归纳於根模组下,也就是说 AppModule 会多引入 TodoController

import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { TodoController } from './todo/todo.controller';

@Module({
  imports: [],
  controllers: [AppController, TodoController],
  providers: [AppService],
})
export class AppModule {}

路由

在建置完 Controller 基本骨架後,会发现 todo.controller.ts@Controller 多了一个字串 todo,这是路由的 前缀 (prefix)

import { Controller } from '@nestjs/common';

@Controller('todo')
export class TodoController {}

注意:透过 NestCLI 建立的 Controller 前缀预设使用该 Controller 的名称,通常会习惯把名称取单数,而前缀改为复数。

添加路由前缀的好处是可以使相同路由的资源都归纳在同一个 Controller 里面,其中包含了前缀底下的所有子路由:
https://ithelp.ithome.com.tw/upload/images/20210304/20119338Y3BtOcUSDt.png

Http Methods

可以透过添加装饰器在 class 的方法上,来指定不同 Http Method 所呼叫的方法,Nest 会根据 Controller 与指定的 Http Method 装饰器来建立路由表。以下方程序码为例,使用的为 GET

import { Controller, Get } from '@nestjs/common';

@Controller('todos')
export class TodoController {
  @Get()
  getAll() {
    return [];
  }
}

使用浏览器进入 http://localhost:3000/todos 来查看结果:
https://ithelp.ithome.com.tw/upload/images/20210321/20119338AV8qBUugaH.png

Nest 的 Http Method 装饰器名称即对应标准 Http Method,这里做了些归纳:

  • @Get:表示接收对应路由且为 GET 请求时触发。
  • @Post:表示接收对应路由且为 POST 请求时触发。
  • @Put:表示接收对应路由且为 PUT 请求时触发。
  • @Patch:表示接收对应路由且为 PATCH 请求时触发。
  • @Delete:表示接收对应路由且为 DELETE 请求时触发。
  • @Options:表示接收对应路由且为 OPTIONS 请求时触发。
  • @Head:表示接收对应路由且为 HEAD 请求时触发。
  • @All:表示接收对应路由且为以上任何方式的请求时触发。

子路由

在设计路由时,很有可能会有子路由的需求,比如说:/todos 底下还有一个取得范例的资源,定义为 GET /todos/examples,但不可能每次有子路由都建立一个新的 Controller,这时候就可以透过 Http Method 装饰器指定子路由,会基於 Controller 设置的前缀来建立,以下方程序码为例,取得资源的路由为 GET /todos/examples

import { Controller, Get } from '@nestjs/common';

@Controller('todos')
export class TodoController {
  @Get('/examples')
  getExample() {
    return [
      {
        id: 1,
        title: 'Example 1',
        description: ''
      }
    ];
  }
}

使用浏览器进入 http://localhost:3000/todos/examples 查看结果:
https://ithelp.ithome.com.tw/upload/images/20210321/20119338QJKDhS5rxx.png

通用路由符号

注意:该用法使用上需要谨慎,基本上除了 ? 以外,都是被允许的字元。

有时候设计路由时,可能会提供些许的容错空间,比如说:原本是 GET /todos/examples,但不管是 /todos/exammmmmmmmples 还是 /todos/exam_ples 都可以得到 /todos/examples 的结果,主要是在指定路由时,使用了 *。下方为范例程序码:

import { Controller, Get } from '@nestjs/common';

@Controller('todos')
export class TodoController {
  @Get('exam*ples')
  get() {
    return [
      {
        id: 1,
        title: 'Example 1',
        description: ''
      }
    ];
  }
}

使用浏览器进入 http://localhost:3000/todos/exammmmmmmmples 查看结果:
https://ithelp.ithome.com.tw/upload/images/20210321/20119338mpLJR0C4Dv.png

路由参数 (Path Parameters)

路由参数的设计十分简单,会在 Http Method 装饰器上做定义,字串格式为 :<PARAMETER_NAME>,接着要在该方法中添加带有 @Param 装饰器的参数,这样就可以顺利取得路由参数。这里我们新增一个路由参数为 id 的路由,程序码如下:

import { Controller, Get, Param } from '@nestjs/common';

@Controller('todos')
export class TodoController {
  @Get(':id')
  get(@Param() params: { id: string }) {
    const { id } = params;
    return {
      id,
      title: `Title ${id}`,
      description: ''
    };
  }
}

这里还有另一种方式可以取得特定路由参数,就是在 @Param 带入指定参数名称:

import { Controller, Get, Param } from '@nestjs/common';

@Controller('todos')
export class TodoController {
  @Get(':id')
  get(@Param('id') id: string) {
    return {
      id,
      title: `Title ${id}`,
      description: ''
    };
  }
}

使用浏览器进入 http://localhost:3000/todos/1 查看结果:
https://ithelp.ithome.com.tw/upload/images/20210321/20119338gdLrnUsRRy.png

查询参数 (Query Parameters)

查询参数与路由参数取得的方式很相似,但不需要在 Http Method 装饰器中做任何设置,只需要在方法中添加带有 @Query 的参数即可。这里我们做一个简单的范例:

import { Controller, Get, Query } from '@nestjs/common';

@Controller('todos')
export class TodoController {
  @Get()
  getList(@Query() query: { limit: number, skip: number }) {
    const { limit = 30, skip = 0 } = query;
    const list = [
      {
        id: 1,
        title: 'Title 1',
        description: ''
      },
      {
        id: 2,
        title: 'Title 2',
        description: ''
      }
    ];

    return list.slice(skip, limit);
  }

}

也可以取得特定查询参数,就是在 @Query 带入指定参数名称:

import { Controller, Get, Query } from '@nestjs/common';

@Controller('todos')
export class TodoController {
  @Get()
  getList(
    @Query('limit') limit: number = 30,
    @Query('skip') skip: number = 0
  ) {
    const list = [
      {
        id: 1,
        title: 'Title 1',
        description: ''
      },
      {
        id: 2,
        title: 'Title 2',
        description: ''
      }
    ];

    return list.slice(skip, limit);
  }
}

使用浏览器进入 http://localhost:3000/todos?limit=1 查看结果:
https://ithelp.ithome.com.tw/upload/images/20210321/20119338OE2TcjIxkQ.png

状态码 (Http Code)

预设情况下,除了 POST 会回传 201 外,大多数的 Http Method 都是回传 200,不过应该要以实际情况来回传适当的状态码。Nest 提供了状态码的 enum,并用装饰器来设置回传的状态码,十分贴心!下方为范例程序码:

import { Controller, Patch, HttpCode, HttpStatus } from '@nestjs/common';

@Controller('todos')
export class TodoController {
  @Patch()
  @HttpCode(HttpStatus.NO_CONTENT)
  get() {
    return [];
  }
}

这里用 Postman 来查看结果:
https://ithelp.ithome.com.tw/upload/images/20210307/20119338SMhRWZ9bqo.png

小结

今天的内容稍微多了些,这边帮大家懒人包一下:

  1. Controller 负责路由及处理来自客户端的请求。
  2. Controller 可以将相同路径下的资源整合在一起,包含子路由。
  3. 透过 Http Method 装饰器轻松配置对应的资源。
  4. 透过 @Param 取得路由参数、透过 @Query 取得查询参数。
  5. 透过 @HttpCodeHttpStatus 来配置该资源回传的状态码。

Controller 的功能非常多,如果都放在同一篇的话篇幅会过长,所以剩下的部分会在下一篇继续说明。


<<:  # Day3--向左走?向右走?再走一次?那些替你做决定的小事

>>:  Day05:就像是刷牙洗脸倒垃圾

[Day01] 程序菜鸟自学C++资料结构 – 前言导读

大家好,我是Crous,随着年纪增长,就业和出路的问题在脑里挥之不去,在今年夏天决定告别委靡不振的暑...

Day 23 - SwiftUI开发实作2 (多爱女朋友测试APP、Alert用法、传递变数)

今天一样是实作,不过今天实作就比较稍微不一样,我们会先讲alert的用法,并且讲解如何在不同View...

[Cmoney 菁英软件工程师战斗营] IOS APP 菜鸟开发笔记(7)----自定义弹出视窗

前言 因为UI和UX方面的需求,这几天上网搜寻了如何自定义下一页的弹出大小,弹出位置和动画,发现有蛮...

Day 23 Password Attacks - 密码攻击 (hydra, pw-inspector)

工具介绍 今天要体验的工具是hydra,有别於先前体验过的其他工具,虽然也是透过字典档的形式,但它支...

大共享时代系列_024_可协同 UI 设计的软件

客户:我的需求不多,就一点点... 设计师:(已预知接下来无穷尽的...浩瀚宇宙) 多人可同时协同设...