【NestJS系列】核心概念:Controller控制器

2023-07-19 18:01:18

前言

控制器主要是用來處理使用者端傳入的請求並向用戶端返回響應。

它一般是用來做路由導航的,內部路由機制控制哪個控制器接收哪些請求。

路由

為了建立基本控制器,我們需要使用@Controller裝飾器,裝飾器將類與所需後設資料關聯起來,並使Nest能夠建立路由對映。

我們使用nest-cli快速建立一個REST API風格的完整CURD程式碼。

nest g resource nanjiu

在生成的nanjiu資料夾下,我們可以看到有nanjiu.controller.ts檔案,程式碼如下:

// nanjiu.controller.ts
import { Controller, Get, Post, Body, Patch, Param, Delete, Query } from '@nestjs/common';
import { NanjiuService } from './nanjiu.service';
import { CreateNanjiuDto } from './dto/create-nanjiu.dto';
import { UpdateNanjiuDto } from './dto/update-nanjiu.dto';

@Controller('nanjiu')
export class NanjiuController {
  constructor(private readonly nanjiuService: NanjiuService) {}

  @Post()
  create(@Body() createNanjiuDto: CreateNanjiuDto) {
    return this.nanjiuService.create(createNanjiuDto);
  }

  @Get()
  findAll(@Param() params, @Query() query) {
    console.log('find', query)
    return this.nanjiuService.findAll();
  }

  @Get(':id')
  findOne(@Param('id') id: string) {
    return this.nanjiuService.findOne(+id);
  }

  @Patch(':id')
  update(@Param('id') id: string, @Body() updateNanjiuDto: UpdateNanjiuDto) {
    return this.nanjiuService.update(+id, updateNanjiuDto);
  }

  @Delete(':id')
  remove(@Param('id') id: string) {
    return this.nanjiuService.remove(+id);
  }
}

@controller裝飾器中傳入了nanjiu引數,表示指定路由字首nanjiu,在@controller裝飾器中使用路由字首,可以讓我們很輕鬆地將一組相關路由放在一起集中管理。

比如當我們通過get方式請求/nanjiu這個路由時,它應該會走到@get裝飾器修飾的findAll方法內

可以使用ApiFox工具進行介面測試:

從上圖中可以看到我此時的請求路徑是http://localhost:3000/apinanjiu,是不是很好奇為了什麼多了一層/api,這是因為我加了一層全域性路由字首

// main.ts
app.setGlobalPrefix('api'); // 全域性路由字首

這裡我們還可以看到狀態碼為200,並且能夠看到findAll的返回值,就說明此時的請求是正常的,但右邊還有個error提示返回資料結構與介面定義不一致。

這是因為這裡我們只是簡單地返回了一個字串,並不符合JSON格式

Nest中,有兩種選項來處理響應值:

  • 標準模式:使用此內建方法,當請求處理程式返回 JavaScript 物件或陣列時,它將自動序列化為 JSON。然而,當它返回 JavaScript 基本型別(例如,stringnumberboolean)時,Nest 將僅傳送該值,而不嘗試對其進行序列化。這使得響應處理變得簡單:只需返回值,Nest 就會處理其餘的事情。

    此外,預設情況下,響應的狀態程式碼始終為 200,除了使用 201 的 POST 請求。我們可以通過@HttpCode(...)在處理程式級別新增裝飾器來輕鬆更改此行為

  • 特定庫模式:我們可以使用特定於庫的(例如,Express)響應物件@Res(),可以使用方法處理程式簽名中的裝飾器注入該物件(例如, findAll(@Res() response))。通過這種方法,您可以使用該物件公開的本機響應處理方法。例如,使用 Express,可以使用response.status(200).send().

路由萬用字元

Nest還支援基於模式的路由,比如,使用萬用字元

@Get('ab*cd')
findAll() {
  return 'This route uses a wildcard';
}

路由'ab*cd'路徑將匹配abcdab_cdabecd等。字元?+*()可以在路由路徑中使用,並且是其正規表示式對應項的子集。連字元 ( -) 和點 ( .) 按字面意思解釋為基於字串的路徑。

請求物件

作為後端專案,存取使用者端請求的詳細資訊也非常重要,從上面生成的程式碼中我們可以看到@Body@Param@Query等裝飾器,沒錯,在大多數時候我們並不需要手動獲取請求物件(查詢字串、引數、請求頭、正文等),直接通過這些開箱即用的裝飾器就能快速獲取。

比如我們在findAll內加上紀錄檔

// nanjiu.controller.ts
@Get()
  findAll(@Param() params, @Query() query) {
    console.log('find', params, query) // 紀錄檔
    return this.nanjiuService.findAll();
  }

然後在請求時帶上一些引數:

此時我們再來看看後端列印的紀錄檔:

這裡就能看到前端請求傳過來的Query引數為city: shanghai

這些開箱即用的裝飾器有以下這些:

@Request(), @Req() req
@Response(), @Res()* res
@Next() next
@Session() req.session
@Param(key?: string) req.params/req.params[key]
@Body(key?: string) req.body/req.body[key]
@Query(key?: string) req.query/req.query[key]
@Headers(name?: string) req.headers/req.headers[name]
@Ip() req.ip
@HostParam() req.hosts

HTTP請求方法

從上面生成的程式碼中的,我們可以發現除了@Get請求方法裝飾器外還有一些其它的,事實上,Nest為所有標準 HTTP 方法提供了裝飾器:@Get()@Post()@Put()@Delete()@Patch()@Options()@Head()

一般大家常用的都是get請求與post請求吧,好像很少看到其它型別的請求

獲取get請求引數

這裡可以使用@Request裝飾器與@Query裝飾器,與express完全一致

上面已經演示了通過@Query獲取,那就在通過@Request再演示一遍

// nanjiu.controller.ts
@Get()
  findAll(@Request() req, @Query() query) {
    console.log('find', req.query, query)
    return this.nanjiuService.findAll();
  }

通過req.queryQuery獲取的是一致的,所以使用@Query裝飾器獲取get型別的請求引數會更方便一些,如果你還想獲取更多關於請求的引數可以使用@Request裝飾器。

獲取post請求引數

express一樣,可以使用@Request裝飾器與Body裝飾器

// nanjiu.controller.ts
@Post()
  create(@Body() createNanjiuDto: CreateNanjiuDto) {
    console.log('body', createNanjiuDto)
    return this.nanjiuService.create(createNanjiuDto);
  }

檢視紀錄檔

動態路由

當需要接受動態資料作為請求的一部分(例如,GET /nanjiu/1獲取帶有 id 為 1nanjiu)時,具有靜態路徑的路由將不起作用。為了定義帶引數的路由,我們可以在路由的路徑中新增路由引數**標記,以捕獲請求 URL 中該位置的動態值。**下面裝飾器範例中的路由引數標記@Get()演示了這種用法。以這種方式宣告的路由引數可以使用裝飾器來存取@Param()

// nanjiu.controller.ts
@Get(':id')
  findOne(@Param() params) {
    console.log('params', params)
    return this.nanjiuService.findOne(+params.id);
  }

檢視紀錄檔

狀態碼

從上面幾個例子我們可以看到,預設情況下響應狀態碼都是200POST請求除外,POST預設為201Nest同樣提供了HttpCode()裝飾器來自定義響應狀態碼

// nanjiu.controller.ts
@Get()
  @HttpCode(202)
  findAll(@Request() req, @Query() query) {
    console.log('find', req, query)
    return this.nanjiuService.findAll();
  }

響應頭

想要自定義響應頭,可以使用@Header裝飾器

@Post()
  @Header('Cache-Control', 'none')
  create(@Body() createNanjiuDto: CreateNanjiuDto) {
    console.log('body', createNanjiuDto)
    return this.nanjiuService.create(createNanjiuDto);
  }