2. Nest:Controller
控制器
控制器負責處理傳入的?請求?和向客戶端返回?響應(yīng)?。
轉(zhuǎn)存失敗重新上傳取消
控制器的目的是接收應(yīng)用的特定請求。路由機制控制哪個控制器接收哪些請求。通常,每個控制器有多個路由,不同的路由可以執(zhí)行不同的操作。
為了創(chuàng)建一個基本的控制器,我們必須使用裝飾器。裝飾器將類與所需的元數(shù)據(jù)關(guān)聯(lián),并使?Nest?能夠創(chuàng)建路由映射(將請求綁定到相應(yīng)的控制器)。
路由
在下面的例子中,我們使用了定義基本控制器所需的?@Controller('cats')?裝飾器。我們將可選前綴設(shè)置為?cats。使用前綴可以避免在所有路由共享通用前綴時出現(xiàn)沖突的情況。我們將使用?@Controller()?裝飾器,這是定義基本控制器所必需的。我們將指定一個路徑前綴(可選)?cats。在?@Controller()?裝飾器中使用路徑前綴,它允許我們輕松對一組相關(guān)路由進行分組,并減少重復代碼。例如,我們可以選擇管理該路由下的客戶實體的交互的這部分進行分組?/customers?,這樣, 我們可以在?@Controller()?裝飾器中指定路徑前綴, 這樣我們就不必為文件中的每個路由重新定義前綴。
cats.controller.ts
import { Controller, Get } from '@nestjs/common';@Controller('cats') export class CatsController {@Get()findAll(): string {return 'This action returns all cats';} }Copy to clipboardErrorCopied要使用 CLI 創(chuàng)建控制器,只需執(zhí)行?$ nest g controller cats?命令。
findAll()方法之前的?@Get()?HTTP?請求方法裝飾器告訴?Nest?為HTTP請求的特定端點創(chuàng)建處理程序。端點對應(yīng)于?HTTP?請求方法(在本例中為 GET)和路由。什么是路由 ? 處理程序的路由是通過連接為控制器聲明的(可選)前綴和請求裝飾器中指定的任何路由來確定的。由于我們已經(jīng)為每個?route(cats) 聲明了一個前綴,并且沒有在裝飾器中添加任何路由信息,因此 Nest會將?GET /cats?請求映射到此處理程序。如上所述,該路由包括可選的控制器路由前綴和請求方法裝飾器中聲明的任何路由。例如,customers?與裝飾器組合的路由前綴?@Get('profile')?會為請求生成路由映射?GET /customers/profile。
在上面的示例中,當對此端點發(fā)出?GET?請求時,Nest?會將請求路由到我們的用戶定義?findAll()?方法。請注意,我們在此處選擇的函數(shù)名稱完全是任意的。我們顯然必須聲明一個綁定路由的函數(shù),但?Nest?不會對所選的函數(shù)名稱附加任何意義。
此函數(shù)將返回?200?狀態(tài)代碼和相關(guān)的響應(yīng),在這種情況下只返回了一個字符串。為什么會這樣? 我們將首先介紹?Nest?使用兩種不同的操作響應(yīng)選項的概念:
| 標準(推薦) | 使用這個內(nèi)置方法,當請求處理程序返回一個?JavaScript?對象或數(shù)組時,它將自動序列化為?JSON。但是,當它返回一個?JavaScript?基本類型(例如string、number、boolean)時,Nest?將只發(fā)送值,而不嘗試序列化它。這使響應(yīng)處理變得簡單:只需要返回值,其余的由?Nest負責。 |
| ? | 此外,響應(yīng)的狀態(tài)碼默認情況下始終為?200,但使用?201?的?POST請求除外。我們可以通過在處理程序級別添加?@HttpCode(...)?裝飾器來輕松更改此行為 (狀態(tài)代碼) |
| 類庫特有的 | 我們可以在函數(shù)簽名通過?@Res()?注入類庫特定的 響應(yīng)對象(例如,Express),使用此函數(shù),您具有使用該對象的響應(yīng)處理函數(shù)。例如,使用?Express,您可以使用類似代碼構(gòu)建響應(yīng)?response.status(200).send() |
注意! 禁止同時使用這兩種方法。?Nest?檢測處理程序是否正在使用?@Res()或?@Next(),如果兩個方法都用了的話, 那么在這里的標準方式就是自動禁用此路由, 你將不會得到你想要的結(jié)果。
Request
許多端點需要訪問客戶端的請求細節(jié)。實際上,Nest?正使用類庫特有(默認是express)的請求對象。因此,我們可以強制?Nest?使用?@Req()?裝飾器將請求對象注入處理程序。
cats.controller.ts
import { Controller, Get, Req } from '@nestjs/common'; import { Request } from 'express';@Controller('cats') export class CatsController {@Get()findAll(@Req() request: Request): string {return 'This action returns all cats';} }Copy to clipboardErrorCopied為了在?express?中使用?Typescript?(如?request: Request?上面的參數(shù)示例所示),請安裝?@types/express?。
Request?對象表示?HTTP?請求,并具有?Request?查詢字符串,參數(shù),HTTP?標頭 和 正文的屬性(在這里),但在大多數(shù)情況下, 不必手動獲取它們。 我們可以使用專用的裝飾器,比如開箱即用的?@Body()?或?@Query()?。 下面是裝飾器和 普通表達對象的比較。
| @Request() | req |
| @Response() | 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] |
為了與底層?HTTP平臺(如?Express和?Fastify)之間的類型兼容,Nest?提供了?@Res()和?@Response()?裝飾器。@Res()只是?@Response()的別名。兩者都直接公開底層響應(yīng)對象接口。在使用它們時,您還應(yīng)該導入底層庫的類型(例如:@types/express)以充分利用它們。注意,在方法處理程序中注入?@Res()或?@Response()?時,將?Nest置于該處理程序的特定于庫的模式中,并負責管理響應(yīng)。這樣做時,必須通過調(diào)用響應(yīng)對象(例如,res.json(…)或?res.send(…))發(fā)出某種響應(yīng),否則HTTP服務(wù)器將掛起。
想要了解如何創(chuàng)建自定義的裝飾器,閱讀這一章。
資源
我們已經(jīng)創(chuàng)建了一個端點來獲取數(shù)據(jù)(GET?路由)。 我們通常還希望提供一個創(chuàng)建新記錄的端點。為此,讓我們創(chuàng)建?POST?處理程序:
cats.controller.ts
import { Controller, Get, Post } from '@nestjs/common';@Controller('cats') export class CatsController {@Post()create(): string {return 'This action adds a new cat';}@Get()findAll(): string {return 'This action returns all cats';} }Copy to clipboardErrorCopied就這么簡單。Nest以相同的方式提供其余的端點裝飾器-?@Put()?、?@Delete()、?@Patch()、?@Options()、?@Head()和?@All()。這些表示各自的?HTTP請求方法。
路由通配符
路由同樣支持模式匹配。例如,星號被用作通配符,將匹配任何字符組合。
@Get('ab*cd') findAll() {return 'This route uses a wildcard'; }Copy to clipboardErrorCopied以上路由地址將匹配?abcd?、?ab_cd?、?abecd?等。字符???、?+?、?*?以及?()?是它們的正則表達式對應(yīng)項的子集。連字符 (-) 和點 (.) 按字符串路徑解析。
狀態(tài)碼
如前面所說,默認情況下,響應(yīng)的狀態(tài)碼總是200,除了 POST 請求外,此時它是201,我們可以通過在處理程序?qū)犹砑?#64;HttpCode(...)?裝飾器來輕松更改此行為。
@Post() @HttpCode(204) create() {return 'This action adds a new cat'; }Copy to clipboardErrorCopiedHttpCode?需要從?@nestjs/common?包導入。
通常,狀態(tài)碼不是固定的,而是取決于各種因素。在這種情況下,您可以使用類庫特有的的響應(yīng)(通過@Res()注入 )對象(或者,在出現(xiàn)錯誤時,拋出異常)。
Headers
要指定自定義響應(yīng)頭,可以使用?@header()?修飾器或類庫特有的響應(yīng)對象,(使用 并?res.header()直接調(diào)用)。
@Post() @Header('Cache-Control', 'none') create() {return 'This action adds a new cat'; }Copy to clipboardErrorCopiedHeader?需要從?@nestjs/common?包導入。
重定向
要將響應(yīng)重定向到特定的?URL,可以使用?@Redirect()裝飾器或特定于庫的響應(yīng)對象(并直接調(diào)用?res.redirect())。
@Redirect()?帶有必需的?url參數(shù)和可選的?statusCode參數(shù)。 如果省略,則?statusCode?默認為?302。
@Get() @Redirect('https://nestjs.com', 301)Copy to clipboardErrorCopied有時您可能想動態(tài)確定HTTP狀態(tài)代碼或重定向URL。通過從路由處理程序方法返回一個形狀為以下形式的對象:
{"url": string,"statusCode": number }Copy to clipboardErrorCopied返回的值將覆蓋傳遞給?@Redirect()裝飾器的所有參數(shù)。 例如:
@Get('docs') @Redirect('https://docs.nestjs.com', 302) getDocs(@Query('version') version) {if (version && version === '5') {return { url: 'https://docs.nestjs.com/v5/' };} }Copy to clipboardErrorCopied路由參數(shù)
當您需要接受動態(tài)數(shù)據(jù)作為請求的一部分時(例如,使用GET?/cats/1來獲取?id為?1的?cat),帶有靜態(tài)路徑的路由將無法工作。為了定義帶參數(shù)的路由,我們可以在路由中添加路由參數(shù)標記,以捕獲請求?URL?中該位置的動態(tài)值。@Get()?下面的裝飾器示例中的路由參數(shù)標記演示了此用法。可以使用?@Param()?裝飾器訪問以這種方式聲明的路由參數(shù),該裝飾器應(yīng)添加到函數(shù)簽名中。
@Get(':id') findOne(@Param() params): string {console.log(params.id);return `This action returns a #${params.id} cat`; }Copy to clipboardErrorCopied@Param()用于修飾方法參數(shù)(上面示例中的參數(shù)),并使路由參數(shù)可用作該修飾的方法參數(shù)在方法體內(nèi)的屬性。 如上面的代碼所示,我們可以通過引用?params.id來訪問?id參數(shù)。 您還可以將特定的參數(shù)標記傳遞給裝飾器,然后在方法主體中按名稱直接引用路由參數(shù)。
Param?需要從?@nestjs/common?包導入。
@Get(':id') findOne(@Param('id') id): string {return `This action returns a #${id} cat`; }Copy to clipboardErrorCopied范圍
對于來自不同編程語言背景的人來說,了解在?Nest?中幾乎所有內(nèi)容都可以在傳入的請求之間共享,這讓人意外。比如我們有一個數(shù)據(jù)庫連接池,具有全局狀態(tài)的單例服務(wù)等。請記住,Node.js?不遵循請求/響應(yīng)多線程無狀態(tài)模型,每個請求都由主線程處理。因此,使用單例實例對我們的應(yīng)用程序來說是完全安全的。
但是,存在基于請求的控制器生命周期可能是期望行為的邊緣情況,例如?GraphQL?應(yīng)用程序中的請求緩存,比如請求跟蹤或多租戶。在這里學習如何控制范圍。
Async / await
我們喜歡現(xiàn)代?JavaScript,而且我們知道數(shù)據(jù)讀取大多是異步的。 這就是為什么?Nest?支持?async?并且與他們一起工作得非常好。
了解更多關(guān)于?Async / await?請點擊這里
每個異步函數(shù)都必須返回?Promise。這意味著您可以返回延遲值, 而?Nest?將自行解析它。讓我們看看下面的例子:
cats.controller.ts
@Get() async findAll(): Promise<any[]> {return []; }Copy to clipboardErrorCopied這是完全有效的。此外,通過返回?RxJS?observable 流。?Nest?路由處理程序更強大。Nest?將自動訂閱下面的源并獲取最后發(fā)出的值(在流完成后)。
cats.controller.ts
@Get() findAll(): Observable<any[]> {return of([]); }Copy to clipboardErrorCopied上面的方法都可以, 你可以選擇你喜歡的方式。
請求負載
之前的?POST?路由處理程序不接受任何客戶端參數(shù)。我們在這里添加?@Body()?參數(shù)來解決這個問題。
首先(如果您使用?TypeScript),我們需要確定?DTO(數(shù)據(jù)傳輸對象)模式。DTO是一個對象,它定義了如何通過網(wǎng)絡(luò)發(fā)送數(shù)據(jù)。我們可以通過使用?TypeScript接口或簡單的類來完成。令人驚訝的是,我們在這里推薦使用類。為什么?類是JavaScript?ES6標準的一部分,因此它們在編譯后的?JavaScript中保留為實際實體。另一方面,由于?TypeScript接口在轉(zhuǎn)換過程中被刪除,所以?Nest不能在運行時引用它們。這一點很重要,因為諸如管道之類的特性在運行時能夠訪問變量的元類型時提供更多的可能性。
我們來創(chuàng)建?CreateCatDto?類:
create-cat.dto.ts
export class CreateCatDto {readonly name: string;readonly age: number;readonly breed: string; }Copy to clipboardErrorCopied它只有三個基本屬性。 之后,我們可以在?CatsController中使用新創(chuàng)建的DTO:
cats.controller.ts
@Post() async create(@Body() createCatDto: CreateCatDto) {return 'This action adds a new cat'; }Copy to clipboardErrorCopied處理錯誤
這里有一章關(guān)于處理錯誤(即處理異常)的單獨章節(jié)。
完整示例
下面是一個示例,該示例利用幾個可用的裝飾器來創(chuàng)建基本控制器。 該控制器公開了幾種訪問和操作內(nèi)部數(shù)據(jù)的方法。
cats.controller.ts
import { Controller, Get, Query, Post, Body, Put, Param, Delete } from '@nestjs/common'; import { CreateCatDto, UpdateCatDto, ListAllEntities } from './dto';@Controller('cats') export class CatsController {@Post()create(@Body() createCatDto: CreateCatDto) {return 'This action adds a new cat';}@Get()findAll(@Query() query: ListAllEntities) {return `This action returns all cats (limit: ${query.limit} items)`;}@Get(':id')findOne(@Param('id') id: string) {return `This action returns a #${id} cat`;}@Put(':id')update(@Param('id') id: string, @Body() updateCatDto: UpdateCatDto) {return `This action updates a #${id} cat`;}@Delete(':id')remove(@Param('id') id: string) {return `This action removes a #${id} cat`;} }Copy to clipboardErrorCopied最后一步
控制器已經(jīng)準備就緒,可以使用,但是?Nest?不知道?CatsController?是否存在,所以它不會創(chuàng)建這個類的一個實例。
控制器總是屬于模塊,這就是為什么我們將?controllers?數(shù)組保存在?@module()?裝飾器中。 由于除了根?ApplicationModule,我們沒有其他模塊,所以我們將使用它來介紹?CatsController:
app.module.ts
import { Module } from '@nestjs/common'; import { CatsController } from './cats/cats.controller';@Module({controllers: [CatsController], }) export class AppModule {}Copy to clipboardErrorCopied我們使用?@Module()裝飾器將元數(shù)據(jù)附加到模塊類,Nest?現(xiàn)在可以輕松反映必須安裝的控制器。
類庫特有方式
到目前為止,我們已經(jīng)討論了?Nest?操作響應(yīng)的標準方式。操作響應(yīng)的第二種方法是使用類庫特有的響應(yīng)對象(Response)。為了注入特定的響應(yīng)對象,我們需要使用?@Res()?裝飾器。為了對比差異,我們重寫?CatsController?:
import { Controller, Get, Post, Res, HttpStatus } from '@nestjs/common'; import { Response } from 'express';@Controller('cats') export class CatsController {@Post()create(@Res() res: Response) {res.status(HttpStatus.CREATED).send();}@Get()findAll(@Res() res: Response) {res.status(HttpStatus.OK).json([]);} }Copy to clipboardErrorCopied雖然這種方法有效,并且事實上通過提供響應(yīng)對象的完全控制(標準操作,庫特定的功能等)在某些方面允許更多的靈活性,但應(yīng)謹慎使用。這種方式非常不清晰,并且有一些缺點。 主要是失去了與依賴于?Nest?標準響應(yīng)處理的?Nest?功能的兼容性,例如攔截器和?@HttpCode()?裝飾器。此外,您的代碼可能變得依賴于平臺(因為底層庫可能在響應(yīng)對象上有不同的?API),并且更難測試(您必須模擬響應(yīng)對象等)。
因此,在可能的情況下,應(yīng)始終首選?Nest?標準方法。
總結(jié)
以上是生活随笔為你收集整理的2. Nest:Controller的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 1. Nest Js
- 下一篇: 3. Nest Provider