版本: 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标准方法。