Node.js控制器Controller使用教程
Controller 控制器
控制器負責(zé)處理傳入的請求并向客戶返回響應(yīng)。
一個控制器的目的是接收應(yīng)用程序的特定請求。路由機制控制哪個控制器接收哪些請求。通常,每個控制器有一個以上的路由,不同的路由可以執(zhí)行不同的動作。
為了創(chuàng)建一個基本的控制器,我們使用類和裝飾器。裝飾器將類與所需的元數(shù)據(jù)聯(lián)系起來,并使 Nest 能夠創(chuàng)建一個路由圖(將請求綁定到相應(yīng)的控制器)。
為了快速創(chuàng)建一個內(nèi)置驗證的 CRUD 控制器,你可以使用 CLI 的 CRUD 生成器:nest g resource [name]
路由
在下面的例子中,我們將使用@Controller()
裝飾器,這是定義一個基本控制器所需要的。我們將指定一個可選的路由路徑前綴為cats
。在@Controller()
裝飾器中使用路徑前綴,可以讓我們輕松地將一組相關(guān)的路由分組,并盡量減少重復(fù)的代碼。例如,我們可以選擇將一組管理與客戶實體互動的路由歸入路由/customers
。在這種情況下,我們可以在@Controller()
裝飾器中指定路徑前綴customers
,這樣我們就不必為文件中的每個路由重復(fù)這部分的路徑。
import { Controller, Get } from "@nestjs/common"; @Controller("cats") export class CatsController { @Get() findAll(): string { return "This action returns all cats"; } }
要使用 CLI 創(chuàng)建一個控制器,只需執(zhí)行$ nest g controller cats
命令。
findAll()
方法之前的@Get()
HTTP 請求方法裝飾器,告訴 Nest 為 HTTP 請求的特定端點創(chuàng)建一個處理程序。端點與 HTTP 請求方法(本例中為 GET)和路由路徑相對應(yīng)。
什么是路由路徑?處理程序的路徑是通過連接控制器聲明@Controller('cats')
中的cats
,以及方法裝飾器@Get()
中指定的任何路徑來確定的。由于我們已經(jīng)為每個路由(cats)聲明了一個前綴,并且沒有在裝飾器@Get()
中添加任何路徑信息,Nest 就會將GET /cats
請求映射到findAll()的Get方法
上來。
如前所述,路徑包括@Controller()
路徑前綴和請求方法findAll()
裝飾器@Get()
中配置的路徑。例如,@Controller('customers')
的路徑前綴與裝飾器@Get('profile')
相結(jié)合,將產(chǎn)生一個GET /customers/profile
這樣的路由映射請求。
在我們上面的例子中,當向該端點發(fā)出 GET 請求時,Nest 將該請求路由到我們用戶定義的 findAll()方法。注意,我們在這里選擇的方法名稱是完全任意的。顯然,我們必須聲明一個方法來綁定路由,但 Nest 并不重視所選擇的方法名稱的任何意義。
這個方法將返回一個 200 狀態(tài)代碼和相關(guān)的響應(yīng),在這種情況下,它只是一個字符串。為什么會發(fā)生這種情況?為了解釋,我們首先要介紹一個概念,即 Nest 采用了兩種不同的選項來操作響應(yīng)。
選項 | 描述 |
---|---|
Standard (recommended) | 使用這種內(nèi)置方法,當請求處理程序返回一個 JavaScript 對象或數(shù)組時,它將自動被序列化為 JSON。然而,當它返回一個 JavaScript 原始類型(例如,字符串、數(shù)字、布爾值)時,Nest 將只發(fā)送值,而不嘗試對其進行序列化。這使得響應(yīng)處理變得簡單:只需返回值,Nest 就會處理其余的事情。此外,響應(yīng)的狀態(tài)代碼默認總是 200,除了使用 201 的 POST 請求。我們可以通過在處理程序級別添加@HttpCode(…)裝飾器來輕松改變這一行為(見狀態(tài)代碼)。 |
Library-specific | 我們可以使用庫特定的(例如 Express)響應(yīng)對象,它可以使用方法處理簽名中的@Res()裝飾器注入(例如 findAll(@Res() response))。通過這種方法,你有能力使用該對象所暴露的本地響應(yīng)處理方法。例如,在 Express 中,你可以使用 response.status(200).send()這樣的代碼來構(gòu)造響應(yīng)。 |
警告:
NEST 會檢測處理程序是否使用@Res()或@Next(),表明你選擇了庫的特定選項。如果同時使用這兩種方法,標準方法將自動禁用于該單一路由,并不再按預(yù)期工作。要同時使用這兩種方法(例如,通過注入響應(yīng)對象只設(shè)置 cookie/頭文件,但仍將其余部分留給框架),你必須在@Res({ passthrough: true })裝飾器中將 passthrough 選項設(shè)置為 true。
請求對象
處理程序經(jīng)常需要訪問客戶端的請求細節(jié)。Nest 提供了對底層平臺(默認為 Express)的請求對象的訪問。我們可以通過在處理程序的簽名中添加@Req()裝飾器來指示 Nest 注入請求對象來訪問它。
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"; } }
為了利用表達式類型的優(yōu)勢(如上面的 request: Request 參數(shù)的例子),請安裝@types/express 包。
請求對象代表了 HTTP 請求,并有請求查詢字符串、參數(shù)、HTTP 頭和正文的屬性(在此閱讀更多內(nèi)容)。在大多數(shù)情況下,沒有必要手動抓取這些屬性。我們可以使用專門的裝飾器來代替,比如@Body()
或@Query()
,這些裝飾器開箱即用。下面是一個所提供的裝飾器的列表,以及它們所代表的普通平臺特定對象。
裝飾器 | 特定對象 |
---|---|
@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 平臺(例如Express
和Fastify
)的類型兼容,Nest 提供了@Res()
和@Response()
裝飾器。@Res()
是 @Response()
的簡單別名。兩者都直接暴露了底層的本地平臺響應(yīng)對象接口。當使用它們時,你也應(yīng)該導(dǎo)入底層庫的類型(例如,@types/express
)以充分利用。請注意,當你在方法處理程序中注入@Res()
或@Response()
時,你將 Nest 放入該處理程序的庫特定模式中,并且你將負責(zé)管理響應(yīng)。當這樣做時,你必須通過調(diào)用響應(yīng)對象(如 res.json(...)
或 res.send(...)
)來發(fā)出某種響應(yīng),否則 HTTP 服務(wù)器會掛起。
如何定義自己的裝飾器,請學(xué)習(xí)這個章節(jié)
資源
早些時候,我們定義了一個方法來獲取cats
的資源(GET 路由)。我們通常也想提供一個創(chuàng)建新記錄的方法。為此,讓我們創(chuàng)建一個 POST 方法。
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"; } }
就是這么簡單。Nest 為所有的標準 HTTP 方法提供裝飾器。 @Get(), @Post(), @Put(), @Delete(), @Patch(), @Options(), 和 @Head()
。此外,@All()
定義了一個可以處理所有這些方法的端點。
路由通配符
基于路由的模式也被支持。例如,*
被用作通配符,將匹配任何字符的組合。
@Get('ab*cd') findAll() { return 'This route uses a wildcard'; }
ab*cd
路由路徑將匹配 abcd、ab_cd、abecd,等等。字符"?“、”+“、”*“和”()"可以在路徑中使用,它們是對應(yīng)于正則表達式的子集。連字符(-)和點(.)可以通過基于字符串的路徑進行字面解釋。
狀態(tài)代碼
如前所述,響應(yīng)狀態(tài)代碼默認總是 200,除了 POST 請求是 201。我們可以通過在處理程序級別添加@HttpCode(...)
裝飾器來輕松改變這一行為。
@Post() @HttpCode(204) create() { return 'This action adds a new cat'; }
從@nestjs/common
包導(dǎo)入HttpCode
通常,你的狀態(tài)代碼不是靜態(tài)的,而是取決于各種因素。在這種情況下,你可以使用一個庫特定的響應(yīng)(使用@Res()注入)對象(或者,在出現(xiàn)錯誤時,拋出一個異常)。
頭信息
要指定一個自定義的響應(yīng)頭,你可以使用@Header()
裝飾器或一個庫特定的響應(yīng)對象(并直接調(diào)用res.header()
)。
@Post() @Header('Cache-Control', 'none') create() { return 'This action adds a new cat'; }
從@nestjs/common
包導(dǎo)入Header
重定向
要將一個響應(yīng)重定向到一個特定的 URL,你可以使用@Redirect()
裝飾器或一個庫特定的響應(yīng)對象(并直接調(diào)用res.redirect()
)。
@Redirect()
需要兩個參數(shù),url
和statusCode
,都是可選的。如果省略的話,statusCode
的默認值是302
。
@Get() @Redirect('https://nestjs.com', 301)
有時你可能想動態(tài)地確定 HTTP 狀態(tài)代碼或重定向 URL。通過從路由處理方法中返回一個對象來做到這一點,類似如下。
{ "url": string, "statusCode": number }
返回的值將覆蓋傳遞給@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/' }; } }
路由參數(shù)
當你需要接受動態(tài)數(shù)據(jù)作為請求的一部分時,帶有靜態(tài)路徑的路由將無法工作(例如,GET /cats/1 以獲得 id 為 1 的貓)。為了定義帶參數(shù)的路由,我們可以在路由的路徑中添加路由參數(shù)令牌,以捕獲請求 URL 中該位置的動態(tài)值。下面@Get()裝飾器例子中的路由參數(shù)令牌展示了這種用法。以這種方式聲明的路由參數(shù)可以使用@Param()裝飾器進行訪問,它應(yīng)該被添加到方法簽名中。
@Get(':id') findOne(@Param() params): string { console.log(params.id); return `This action returns a #${params.id} cat`; }
@Param()
被用來裝飾一個方法參數(shù)(上面例子中的params
),并使路由參數(shù)作為該方法主體中被裝飾的方法參數(shù)的屬性可用。正如上面的代碼所見,我們可以通過引用params.id
來訪問id參數(shù)
。你也可以向裝飾器傳遞一個特定的參數(shù)標記,然后在方法主體中直接引用路由參數(shù)的名稱。
從@nestjs/common
包導(dǎo)入Param
@Get(':id') findOne(@Param('id') id: string): string { return `This action returns a #${id} cat`; }
子域路由
@Controller
裝飾器可以接受一個host
選項,要求傳入的請求的 HTTP 主機與某些特定的值相匹配。
@Controller({ host: "admin.example.com" }) export class AdminController { @Get() index(): string { return "Admin page"; } }
由于 Fastify 缺乏對嵌套路由器的支持,當使用子域路由時,應(yīng)該使用(默認)Express 適配器來代替
與路由路徑類似,hosts
選項可以使用令牌來捕獲主機名稱中該位置的動態(tài)值。下面@Controller()
裝飾器例子中的主機參數(shù)令牌展示了這種用法。以這種方式聲明的主機參數(shù)可以使用@HostParam()
裝飾器進行訪問,它應(yīng)該被添加到方法簽名中。
@Controller({ host: ":account.example.com" }) export class AccountController { @Get() getInfo(@HostParam("account") account: string) { return account; } }
范疇
對于來自不同編程語言背景的人來說,要知道在 Nest 中,幾乎所有的東西都是在傳入的請求中共享的,這可能是意想不到的。我們有一個到數(shù)據(jù)庫的連接池,有全局狀態(tài)的單體服務(wù),等等。請記住,Node.js 并不遵循請求/響應(yīng)的多線程無狀態(tài)模型,其中每個請求都由一個單獨的線程來處理。因此,使用單體實例對我們的應(yīng)用程序是完全安全的。
但是,在極端情況下,基于請求的控制器生存周祁可能是所需的行為(However, there are edge-cases when request-based lifetime of the controller may be the desired behavior),例如 GraphQL 應(yīng)用程序中的每個請求緩存,請求跟蹤或多租戶。在這里了解如何控制作用域。
異步性
我們熱愛現(xiàn)代 JavaScript,我們知道數(shù)據(jù)提取大多是異步的。這就是為什么 Nest 支持并能很好地使用異步函數(shù)。
學(xué)習(xí)async / await
請點擊這里
每個異步函數(shù)都必須返回一個 Promise。這意味著你可以返回一個延遲值,Nest 將能夠自己解決。讓我們來看看這個例子。
@Get() async findAll(): Promise<any[]> { return []; }
上述代碼是完全有效的。此外,Nest 路由處理程序通過能夠返回 RxJS 可觀察流而變得更加強大。Nest 將自動訂閱下面的源并獲取最后一個發(fā)出的值(一旦流完成)–( Nest will automatically subscribe to the source underneath and take the last emitted value (once the stream is completed))
@Get() findAll(): Observable<any[]> { return of([]); }
上述兩種方法都有效,你可以使用任何符合你要求的方法。
請求的有效載荷
我們之前的 POST 路由處理器的例子沒有接受任何客戶端參數(shù)。讓我們通過在這里添加@Body()
裝飾器來解決這個問題。
但首先(如果你使用 TypeScript),我們需要確定 DTO(數(shù)據(jù)傳輸對象)模式。DTO 是一個定義了數(shù)據(jù)如何在網(wǎng)絡(luò)上發(fā)送的對象。我們可以通過使用 TypeScript 接口,或通過簡單的類來確定 DTO 模式。有趣的是,我們在這里推薦使用類。為什么呢?類是 JavaScript ES6 標準的一部分,因此它們在編譯后的 JavaScript 中被保留為真實的實體。另一方面,由于 TypeScript 接口在轉(zhuǎn)譯過程中被移除,Nest 不能在運行時引用它們。這一點很重要,因為像管道這樣的功能在運行時可以訪問變量的元類型,從而實現(xiàn)更多的可能性。
我們來創(chuàng)建CreateCatDto
類。
export class CreateCatDto { name: string; age: number; breed: string; }
它只有三個基本屬性。此后我們可以在CatsController
中使用新創(chuàng)建的DTO
。
我們的 Validation Pipe 可以過濾掉那些不應(yīng)該被方法處理程序接收的屬性。在這種情況下,我們可以將可接受的屬性列入白名單,任何不包括在白名單中的屬性都會自動從結(jié)果對象中剝離。在CreateCatDto
的例子中,我們的白名單是名稱、年齡和品種屬性。在這里了解更多。
處理錯誤
這里有一個關(guān)于處理錯誤的單獨章節(jié)(即,與異常一起工作)。
完整例子
下面是一個例子,利用幾個可用的裝飾器來創(chuàng)建一個基本的控制器。這個控制器暴露了一些方法來訪問和操作內(nèi)部數(shù)據(jù)。
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`; } }
Nest CLI 提供了一個自動生成器(示意圖),自動生成所有的模板代碼,以幫助我們避免做這些事情,并使開發(fā)人員的體驗更加簡單。在這里閱讀更多關(guān)于這個功能的信息。
開始運行
隨著上述控制器的完全定義,Nest 仍然不知道CatsController
的存在,因此不會創(chuàng)建這個類的實例。
控制器總是屬于一個模塊,這就是為什么我們在@Module()
裝飾器中包含控制器數(shù)組。由于我們還沒有定義任何其他模塊,除了根AppModule
,我們將用它來介紹CatsController
。
import { Module } from "@nestjs/common"; import { CatsController } from "./cats/cats.controller"; @Module({ controllers: [CatsController], }) export class AppModule {}
我們使用@Module()
裝飾器將元數(shù)據(jù)附加到模塊類,Nest 現(xiàn)在可以很容易地反映出哪些控制器必須被安裝。
庫的特定方法
到目前為止,我們已經(jīng)討論了操作響應(yīng)的 Nest 標準方式。操作響應(yīng)的第二種方式是使用庫特定的響應(yīng)對象。為了注入一個特定的響應(yīng)對象,我們需要使用 @Res()
裝飾器。為了顯示差異,讓我們把CatsController
重寫成以下內(nèi)容。
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([]); } }
雖然這種方法是可行的,而且事實上通過提供對響應(yīng)對象的完全控制,在某些方面確實有更大的靈活性(頭文件的操作、庫的特定功能等等),但是應(yīng)該謹慎使用。一般來說,這種方法不那么明確,而且確實有一些缺點。主要的缺點是,你的代碼變得依賴于平臺(因為底層庫在響應(yīng)對象上可能有不同的 API),而且更難測試(你必須模擬響應(yīng)對象,等等)。
另外,在上面的例子中,你失去了與依賴 Nest 標準響應(yīng)處理的 Nest 功能的兼容性,如攔截器和@HttpCode() / @Header() 裝飾器。為了解決這個問題,你可以將 passthrough 選項設(shè)置為 true,如下所示。
@Get() findAll(@Res({ passthrough: true }) res: Response) { res.status(HttpStatus.OK); return []; }
現(xiàn)在,你可以與本地響應(yīng)對象進行交互(例如,根據(jù)某些條件設(shè)置 cookies 或頭信息),但把其余的事情留給框架。
到此這篇關(guān)于Node.js控制器Controller使用教程的文章就介紹到這了,更多相關(guān)Node.js Controller內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
WebSocket+node.js創(chuàng)建即時通信的Web聊天服務(wù)器
這篇文章主要為大家詳細介紹了WebSocket+node.js創(chuàng)建即時通信的Web聊天服務(wù)器的相關(guān)資料,感興趣的小伙伴們可以參考一下2016-08-08nodejs中簡單實現(xiàn)Javascript Promise機制的實例
這篇文章主要介紹了nodejs中簡單實現(xiàn)Javascript Promise機制的實例,本文在nodejs中簡單實現(xiàn)一個promise/A 規(guī)范,需要的朋友可以參考下2014-12-12nodejs使用http模塊發(fā)送get與post請求的方法示例
這篇文章主要介紹了nodejs使用http模塊發(fā)送get與post請求的方法,結(jié)合實例形式分析了nodejs基于http模塊實現(xiàn)發(fā)送get與post請求具體操作技巧,需要的朋友可以參考下2018-01-01Node 使用express-http-proxy 做api網(wǎng)關(guān)的實現(xiàn)
這篇文章主要介紹了Node 使用express-http-proxy 做api網(wǎng)關(guān)的實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-10-10Node.js操作mysql數(shù)據(jù)庫增刪改查
這篇文章主要介紹使用Node.js操作mysql數(shù)據(jù)庫增刪改查的相關(guān)資料,需要的朋友可以參考下2016-03-03nodejs個人博客開發(fā)第六步 數(shù)據(jù)分頁
這篇文章主要為大家詳細介紹了nodejs個人博客開發(fā)的數(shù)據(jù)分頁,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-04-04