控制器

控制器层负责处理传入的请求, 并返回对客户端的响应。

图1

要创建一个基本的控制器,你必须将 元数据(metadata) 附加到类中。Nest 元数据知道如何映射您的控制器到适当的路由。要附加元数据到我们正在使用的装饰器 (在这种情况下 @Controller('cats') )。

cats.controller.ts

  1. import { Controller, Get } from '@nestjs/common';
  2. @Controller('cats')
  3. export class CatsController {
  4. @Get()
  5. findAll() {
  6. return [];
  7. }
  8. }

元数据(Metadata)

我们在这里使用 @Controller('cats') 。 这个装饰器是强制性的。 cats 是每个在类中注册的路由的前缀。 前缀是可选的,意味着您可以将圆括号留空 (@Controller()),但是它能减少冗余重复的代码,当要创建一个新的端点(路由)时,你没有必要在别处声明。

有一个公共方法, 即 findAll(), 它返回一个空数组。@Get() 装饰器告诉 Nest, 必须为该路径创建一个端点路由, 并将每个适当的请求映射到此处理程序。由于我们为每个路由(cats)声明了前缀,所以Nest会在这里映射每个 /cats 的 GET 请求。

当客户端调用此端点时, Nest 将返回 200 状态码和解析的 JSON, 因此在本例中-它只是一个空数组。这怎么可能?

两种可能的方法来处理响应:

标准(推荐) 我们对处理程序的处理方式与普通函数相同。当我们返回 JavaScript 对象或数组时, 它会自动转换为 JSON。当我们返回字符串, Nest 将只发送一个字符串。
此外, 响应状态代码在默认情况下总是 200, 除了 POST 请求外,此时它是 201。我们可以通过在处理程序层添加 @HttpCode(...) 装饰器来轻松地更改此行为。
Express 我们可以使用快速响应对象,,例如findAll(@Res()response)。

我们可以使用 「express」 响应对象, 我们可以在函数签名中使用 @Res() 装饰器注入, 例如 findAll (@Res() response)

!> 注意! 禁止同时使用这两种方法。 Nest 检测处理程序是否正在使用 @Res()@Next(),如果是这样,此单个路由的「标准」方式将被禁用。

Request 对象

许多端点需要访问客户端的请求细节。 实际上,Nest 正使用 「express」 请求对象。 我们可以强制 Nest 使用 @Req() 装饰器将请求对象注入处理程序。

!> 有一个 @types/express 包,我们强烈建议使用它(「请求」有它自己的类型)。

cats.controller.ts

  1. import { Controller, Get, Req } from '@nestjs/common';
  2. @Controller('cats')
  3. export class CatsController {
  4. @Get()
  5. findAll(@Req() request) {
  6. return [];
  7. }
  8. }

「Request」对象表示 HTTP 请求,并具有「Request」查询字符串,参数,HTTP 标头 和 正文的属性(在这里阅读更多),但在大多数情况下, 不必手动获取它们。 我们可以使用专用的装饰器,比如 @Body()@Query() ,这些都是现成的。 下面是装饰器和「express」的比较。

@Request() req
@Response() res
@Next() next
@Session() req.session
@Param(param?: string) req.params / req.params[param]
@Body(param?: string) req.body / req.body[param]
@Query(param?: string) req.query / req.query[param]
@Headers(param?: string) req.headers / req.headers[param]

更多端点

我们已经创建了一个端点来获取数据(GET 路由)。 提供创建新记录的方式也是很好的。让我们创建 POST 处理程序:

cats.controller.ts

  1. import { Controller, Get, Post } from '@nestjs/common';
  2. @Controller('cats')
  3. export class CatsController {
  4. @Post()
  5. create() {
  6. // TODO: Add some logic here
  7. }
  8. @Get()
  9. findAll() {
  10. return [];
  11. }
  12. }

这很容易。 Nest以同样的方式提供了其他的端点装饰器 - @Put()@Delete()@Patch()@Options()@Head()@All()

状态码操作

如前面所说,默认情况下,响应的状态码总是200,除了 POST 请求外,此时它是201,我们可以通过在处理程序层添加@HttpCode(...) 装饰器来轻松更改此行为。

cats.controller.ts

  1. import { Controller, Get, Post, HttpCode
  2. } from '@nestjs/common';
  3. @Controller('cats')
  4. export class CatsController {
  5. @HttpCode(204)
  6. @Post()
  7. create() {
  8. // TODO: Add some logic here
  9. }
  10. @Get()
  11. findAll() {
  12. return [];
  13. }
  14. }

路由参数

当需要将动态数据作为 URL 的一部分接受时, 使用静态路径的路由无法提供帮助。要使用路由参数定义路由,只需在路由的路径中指定路由参数,如下所示。

  1. @Get(':id')
  2. findOne(@Param() params) {
  3. console.log(params.id);
  4. return {};
  5. }

Async / await

我们喜欢现代 JavaScript,而且我们知道数据读取大多是异步的。 这就是为什么 Nest 支持异步函数,并且与他们一起工作得非常好。

!> 了解更多关于 Async / await 请点击这里

每个异步函数都必须返回 Promise。这意味着您可以返回延迟值, 而 Nest 将自行解析它。让我们看看下面的例子:

cats.controller.ts

  1. @Get()
  2. async findAll(): Promise<any[]> {
  3. return [];
  4. }

这是完全有效的。

此外, Nest 路由处理程序更强大。它可以返回一个 Rxjs observable 的流,使得在简单的 web 应用程序和 微服务 之间的迁移更加容易。

cats.controller.ts

  1. @Get()
  2. findAll(): Observable<any[]> {
  3. return Observable.of([]);
  4. }

没有推荐的方法。 你可以使用任何你想要的。

POST 处理程序

奇怪的是, 这个 POST 路由处理程序不接受任何客户端参数。我们至少应该在这里等待 @Body() 的参数。

首先, 我们需要建立 DTO (数据传输对象) 模式。DTO 是一个定义如何通过网络发送数据的对象。我们可以使用TypeScript接口或简单的类来完成。令人惊讶的是,我们建议在这里使用类。为什么?这些类是 JavaScript ES6 标准的一部分, 所以它们只是简单的函数。另一方面, TypeScript 接口在编译过程中被删除, Nest 不能引用它们。这一点很重要, 因为管道等功能能够在访问变量的元类型时提供更多的可能性。

我们来创建 CreateCatDto:

dto/create-cat.dto.ts

  1. export class CreateCatDto {
  2. readonly name: string;
  3. readonly age: number;
  4. readonly breed: string;
  5. }

它只有三个基本属性。 所有这些都被标记为只读,因为我们应该尽可能使我们的功能尽可能不被污染。

现在我们可以使用 CatsController 中的模式:

cats.controller.ts

  1. @Post()
  2. async create(@Body() createCatDto: CreateCatDto) {
  3. // TODO: Add some logic here

处理 errors

这里有一个关于处理异常的独立章节

最后一步

控制器已经准备就绪,可以使用,但是 Nest 不知道 CatsController 是否存在,所以它不会创建这个类的一个实例。 我们需要告诉它。

控制器总是属于模块,这就是为什么我们在 @Module() 装饰器中保存控制器数组。 由于除了根ApplicationModule,我们没有其他模块,现在就让我们使用它:

app.module.ts

  1. import { Module } from '@nestjs/common';
  2. import { CatsController } from './cats/cats.controller';
  3. @Module({
  4. controllers: [CatsController],
  5. })
  6. export class ApplicationModule {}

!> 我们将元数据附加到 module 类,所以现在 Nest 可以很容易地反映出哪些控制器必须被安装。

Express 方式

操作响应的第二种方法是使用快速响应对象。 这

操纵响应的第二种方法是使用 express 响应对象。这是 Nest 4 目前唯一可用的选项。要注入响应对象,我们需要使用 @Res() 装饰器。 为了对比差异,我将重写 CatsController:

  1. import { Controller, Get, Post, Res, Body, HttpStatus } from '@nestjs/common';
  2. import { CreateCatDto } from './dto/create-cat.dto';
  3. @Controller('cats')
  4. export class CatsController {
  5. @Post()
  6. create(@Res() res, @Body() createCatDto: CreateCatDto) {
  7. // TODO: Add some logic here
  8. res.status(HttpStatus.CREATED).send();
  9. }
  10. @Get()
  11. findAll(@Res() res) {
  12. res.status(HttpStatus.OK).json([]);
  13. }
  14. }

从我的角度来看,这种方式是非常不清晰。 我当然更喜欢第一种方法,但为了使 Nest 向下兼容以前的版本,这种方法仍然可用。 而且,响应对象提供了更大的灵活性 - 您可以完全控制响应。