版本: 6.10.14
控制器負責處理傳入的請求並將響應返回給客戶端typescript
控制器的目的是接收應用程序的特定請求。路由機制控制哪一個控制器接收哪些請求。一般,每一個控制器都有多個路由,不一樣的路由能夠執行不一樣的操做express
爲了建立一個基本的控制器,咱們使用類和裝飾器。裝飾器將類與所需的元數據關聯,並啓用Nest來建立路由映射(將請求綁定到相應的控制器)。json
在下面的示例中,咱們將使用@Controller()裝飾器,這是定義基本控制器所必需的。咱們將指定cats的可選路由路徑前綴.在@Controller()裝飾器中使用路徑前綴可讓咱們輕鬆地對一組相關路由進行分組,並將重複的代碼最小化。api
例如,咱們能夠選擇對一組路由進行分組,這些路由在route/customers管理下與customers實體類的交互。在這種狀況下,咱們能夠在@Controller()裝飾器中指定路徑前綴customers,這樣就沒必要爲文件中的每一個路由重複該部分路徑數組
import { Controller, Get } from '@nestjs/common'; @Controller('cats') export class CatsController { @Get() findAll(): string { return 'This action returns all cats'; } }
要使用CLI建立控制器,只需執行
nest g controller cats
命令。
findAll()方法以前的@Get()裝飾器, 告訴Nest爲HTTP請求作特殊處理。裝飾器參數對應於HTTP請求方法(本例中爲GET)和路由路徑。路由路徑是什麼?處理程序的路由路徑是經過鏈接爲控制器聲明的(可選)前綴和在請求裝飾器中指定的任何路徑來肯定的。因爲咱們已經爲每一個路由(cats)聲明瞭一個前綴,而且沒有在裝飾器中添加任何路徑信息,Nest將把GET/cats請求映射到此處理程序。如前所述,路徑包括可選的控制器路徑前綴和在請求方法的裝飾器中聲明的任何路徑字符串。例如,customers的路徑前綴與裝飾了的@Get('profile')結合將生成Get/customers/profile等請求的路由映射服務器
在上面的示例中,當該控制器發出GET請求時,Nest將請求路由到用戶定義的findAll()方法。注意,咱們在這裏選擇的方法名是徹底任意的。顯然,咱們必須聲明一個方法來綁定路由,可是Nest對所選的方法名沒有任何意義。也就是說方法名與路由路徑無關。網絡
此方法將返回一個200狀態代碼和相關聯的響應,在本例中只是一個字符串。爲何會這樣?爲了解釋,咱們將首先介紹Nest使用兩種不一樣的選項來操縱響應的概念。session
描述 | |
---|---|
Standard | 使用此內置方法,當請求處理程序返回JavaScript對象或數組時,它將自動序列化爲JSON。然而,當它返回一個JavaScript原語類型(例如string、number、boolean)時,Nest將只發送值而不嘗試序列化它。這使得響應處理變得簡單:只需返回值,Nest就能夠處理其他的部分。此外,除了使用201的POST請求外,默認狀況下響應的狀態代碼始終爲200。咱們能夠經過在處理程序級別添加@HttpCode裝飾來輕鬆更改此行爲(請參閱狀態代碼)。 |
Library-specific | 咱們可使用特定於庫的(例如Express)響應對象,它可使用方法處理程序簽名中的@Res()裝飾器(例如findAll(@Res()response))注入。使用這種方法,您能夠(也有能力)使用該對象暴露的本機響應處理方法。例如,使用Express,可使用相似response.status(200.send()的代碼構造響應 |
警告: 不能同時使用這兩種方法。Nest檢測處理程序什麼時候使用@Res()或@Next(),代表您選擇了Library-specific的選項。若是同時使用兩種方法,則此Standard選項將自動禁用,而且再也不按預期工做。Nest在同時使用了類裝飾器響應請求和參數裝飾器請求時, 以參數裝飾器爲準,類裝飾器將失效,這相似於
就近原則
處理程序一般須要訪問客戶端請求的詳細信息。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'; } }
提示: 爲了更好的使用express(如上面的request:request參數示例所示),請安裝@types/express包來得到提示。
request對象表示HTTP請求,並具備請求查詢字符串、參數、HTTP頭和正文的屬性。在大多數狀況下,不須要手動獲取這些屬性。咱們可使用專用的裝飾器,例如@Body()或@Query(),它們是現成的。下面是提供的裝飾器和它們表示的純平臺特定對象的列表。異步
裝飾器 | 對應對象 |
---|---|
@Request() |
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 |
爲了與底層HTTP平臺(如Express和fastfy)上的類型兼容,Nest提供了@Res()和@Response()裝飾符。@Res()只是@Response()的別名。二者都直接暴露底層的本機平臺響應對象接口。使用它們時,還應導入底層庫的類型(例如,@types/express)以充分利用它們。注意,當您在方法處理程序中注入@Res()或@Response()時,您會將Nest放入該處理程序的特定於庫的模式中,並負責管理響應。執行此操做時,必須經過調用response對象(例如res.json(…)或res.send(…)來發出某種響應,不然HTTP服務器將掛起
提示: 您是否想學習如何製做本身的裝飾器呢(後面將會學習到,暫未翻譯)前往瀏覽 https://docs.nestjs.com/custom-decorators
以前咱們使用了@Get
裝飾器來經過get方法訪問資源,如今咱們來試試用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請求裝飾器-@Put()、@Delete()、@Patch()、@Options()、@Head()和@All()。每一個表示其各自的HTTP請求方法。
也支持通配符模式,例如,星號用做通配符,將匹配任何字符組合。
@Get('ab*cd') findAll() { return 'This route uses a wildcard'; }
這個ab*cd
將會匹配abcd
,ab_cd
,abecd
等等,?
,+
,*
, 和()
也容許在路由路徑中。
如前所述,默認狀況下響應狀態代碼始終爲200,除了post爲201外。咱們能夠很輕鬆的添加@HttpCode裝飾器來改變默認行爲
@Post() @HttpCode(204) create() { return 'This action adds a new cat'; }
先得導包: importHttpCode
from@nestjs/common
要指定自定義響應頭,可使用@header()裝飾器或庫特定的響應對象(並直接調用res.header())
@Post() @Header('Cache-Control', 'none') create() { return 'This action adds a new cat'; }
想要重定向到某一個URL,你可使用@Redirect()裝飾器,也能夠直接使用特定的響應對象如:res.redirect()
@Get() @Redirect('https://nestjs.com', 301)
有時候你須要動態的傳遞參數,你只須要返回的時候遵循以下結構:
{ "url": string, "statusCode": number }
返回的值將重寫傳遞給@Redirect()裝飾器的任何參數。例如:
@Get('docs') @Redirect('https://docs.nestjs.com', 302) getDocs(@Query('version') version) { if (version && version === '5') { return { url: 'https://docs.nestjs.com/v5/' }; } }
當須要接受動態數據做爲請求的一部分時(例如,get/cats/1以獲取id爲1的cat),具備靜態路徑的路由將不起做用。爲了用參數定義路由,咱們能夠在路由路徑中添加路由參數標記,以捕獲請求URL中該位置的動態值。下面@Get()decorator示例中的route參數標記演示了這種用法。以這種方式聲明的路由參數可使用@Param()裝飾器訪問,該裝飾器應該添加到方法相應地方。
@Get(':id') findOne(@Param() params): string { console.log(params.id); return `This action returns a #${params.id} cat`; }
@Param()用於修飾方法參數(上面示例中的params),並使route參數做爲該修飾方法參數在方法主體中的屬性可用。如上面的代碼所示,咱們能夠經過引用params.id來訪問id參數,也能夠將特定的參數標記傳遞給裝飾器,而後在方法體中直接經過名稱引用route參數。
@Get(':id') findOne(@Param('id') id): string { return `This action returns a #${id} cat`; }
@Controller decorator能夠採用host選項,要求傳入請求的HTTP host與某些特定值匹配。
@Controller({ host: 'admin.example.com' }) export class AdminController { @Get() index(): string { return 'Admin page'; } }
警告: 因爲fastfy缺少對嵌套路由器的支持,所以在使用子域路由時,應改用(默認)Express適配器。
與路由路徑相似,hosts選項可使用令牌捕獲host名中該位置的動態值。下面@Controller()裝飾器示例中的主機參數標記演示了這種用法。可使用@HostParam()裝飾器訪問以這種方式聲明的主機參數,該裝飾器應添加到方法簽名中。
@Controller({ host: ':account.example.com' }) export class AccountController { @Get() getInfo(@HostParam('account') account: string) { return account; } }
咱們喜歡現代JavaScript,並且咱們知道數據提取主要是異步的。這就是爲何Nest支持異步函數並能很好地工做
每一個異步函數都必須返回一個承諾。這意味着您能夠返回一個Nest可以自行解析的值。讓咱們看看這個例子.
@Get() async findAll(): Promise<any[]> { return []; }
以上代碼徹底有效! 此外,Nest路由處理程序可以返回RxJS可觀察流,所以功能更增強大。Nest將自動訂閱下面的源並獲取最後一個發出的值(一旦流完成)
@Get() findAll(): Observable<any[]> { return of([]); }
以上兩種方法都有效,您可使用任何符合您要求的方法
咱們以前的POST路由處理程序示例不接受任何客戶端參數。讓咱們經過在@Body()
此處添加裝飾器來解決此問題。
可是首先(若是您使用TypeScript),咱們須要肯定DTO(數據傳輸對象)架構。DTO是定義如何在網絡上發送數據的對象。咱們能夠經過使用TypeScript接口或簡單的類來肯定DTO模式。有趣的是,咱們建議在這裏使用類。爲何?類是JavaScript ES6標準的一部分,所以,它們在編譯的JavaScript中保留爲真實實體。因爲TypeScript接口在編譯過程當中被移除,Nest不能在運行時引用它們,這一點很重要,由於管道(pipe)等功能在運行時能夠訪問變量的元類型.
讓咱們建立CreateCatDto
類
export class CreateCatDto { readonly name: string; readonly age: number; readonly breed: string; }
它只有三個基本屬性。以後,咱們能夠在內使用新建立的DTO CatsController
:
@Post() async create(@Body() createCatDto: CreateCatDto) { return 'This action adds a new cat'; }
下面是一個使用幾個可用的裝飾器建立基本控制器的示例。這個控制器公開了兩個方法來訪問和操做內部數據。
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仍然不知道CatsController
該類是否存在,所以將不會建立此類的實例.
控制器始終屬於一個模塊,這就是爲何咱們controllers
在@Module()
裝飾器中包含該數組的緣由。因爲除了root以外AppModule
,咱們尚未定義其餘任何模塊,所以咱們將使用它來引入CatsController
:
import { Module } from '@nestjs/common'; import { CatsController } from './cats/cats.controller'; @Module({ controllers: [CatsController], }) export class AppModule {}
咱們使用@Module()
裝飾器將元數據附加到模塊類,Nest如今能夠輕鬆反映必須安裝的控制器。
到目前爲止,咱們已經討論了操做響應的Nest標準方法。處理響應的第二種方法是使用特定於庫(express)的響應對象
爲了注入一個特定的響應對象,咱們須要使用@Res()裝飾器,以下(能夠比較下和Nest標準處理方法的不一樣點)
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([]); } }
雖然這種方法是有效的,而且事實上經過提供對響應對象(頭部操做、庫特定特徵等)的徹底控制在某些方面容許更多的靈活性,可是它應該謹慎使用。
總的來講,這種方法不太清晰,並且確實有一些缺點。主要缺點是與依賴於Nest標準響應處理的特性(如攔截器和@HttpCode()裝飾器)不兼容。此外,您的代碼可能依賴於平臺(由於底層庫在響應對象上可能有不一樣的api < 前面提到過的express和fastify>),而且很難測試(您必須模擬響應對象等)。
所以,在可能的狀況下,應始終首選Nest標準方法。