Nestjs实战干货-概况-控制器-Controller

news/2024/4/28 8:24:44/文章来源:https://blog.csdn.net/zwy8831/article/details/130040511

Controller 控制器

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

来自静态目录的图像

一个控制器的目的是接收应用程序的特定请求。路由机制控制哪个控制器接收哪些请求。通常,每个控制器有一个以上的路由,不同的路由可以执行不同的动作。

为了创建一个基本的控制器,我们使用类和装饰器。装饰器将类与所需的元数据联系起来,并使 Nest 能够创建一个路由图(将请求绑定到相应的控制器)。

为了快速创建一个内置验证的 CRUD 控制器,你可以使用 CLI 的 CRUD 生成器:nest g resource [name]

路由

在下面的例子中,我们将使用@Controller()装饰器,这是定义一个基本控制器所需要的。我们将指定一个可选的路由路径前缀为cats。在@Controller()装饰器中使用路径前缀,可以让我们轻松地将一组相关的路由分组,并尽量减少重复的代码。例如,我们可以选择将一组管理与客户实体互动的路由归入路由/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() HTTP 请求方法装饰器,告诉 Nest 为 HTTP 请求的特定端点创建一个处理程序。端点与 HTTP 请求方法(本例中为 GET)和路由路径相对应。

什么是路由路径?处理程序的路径是通过连接控制器声明@Controller('cats')中的cats,以及方法装饰器@Get()中指定的任何路径来确定的。由于我们已经为每个路由(cats)声明了一个前缀,并且没有在装饰器@Get()中添加任何路径信息,Nest 就会将GET /cats请求映射到findAll()的Get方法上来。

如前所述,路径包括@Controller()路径前缀和请求方法findAll()装饰器@Get()中配置的路径。例如,@Controller('customers')的路径前缀与装饰器@Get('profile')相结合,将产生一个GET /customers/profile这样的路由映射请求。

在我们上面的例子中,当向该端点发出 GET 请求时,Nest 将该请求路由到我们用户定义的 findAll()方法。注意,我们在这里选择的方法名称是完全任意的。显然,我们必须声明一个方法来绑定路由,但 Nest 并不重视所选择的方法名称的任何意义。

这个方法将返回一个 200 状态代码和相关的响应,在这种情况下,它只是一个字符串。为什么会发生这种情况?为了解释,我们首先要介绍一个概念,即 Nest 采用了两种不同的选项来操作响应。

选项描述
Standard (recommended)使用这种内置方法,当请求处理程序返回一个 JavaScript 对象或数组时,它将自动被序列化为 JSON。然而,当它返回一个 JavaScript 原始类型(例如,字符串、数字、布尔值)时,Nest 将只发送值,而不尝试对其进行序列化。这使得响应处理变得简单:只需返回值,Nest 就会处理其余的事情。此外,响应的状态代码默认总是 200,除了使用 201 的 POST 请求。我们可以通过在处理程序级别添加@HttpCode(…)装饰器来轻松改变这一行为(见状态代码)。
Library-specific我们可以使用库特定的(例如 Express)响应对象,它可以使用方法处理签名中的@Res()装饰器注入(例如 findAll(@Res() response))。通过这种方法,你有能力使用该对象所暴露的本地响应处理方法。例如,在 Express 中,你可以使用 response.status(200).send()这样的代码来构造响应。

警告:
NEST 会检测处理程序是否使用@Res()或@Next(),表明你选择了库的特定选项。如果同时使用这两种方法,标准方法将自动禁用于该单一路由,并不再按预期工作。要同时使用这两种方法(例如,通过注入响应对象只设置 cookie/头文件,但仍将其余部分留给框架),你必须在@Res({ passthrough: true })装饰器中将 passthrough 选项设置为 true。

请求对象

处理程序经常需要访问客户端的请求细节。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";}
}

为了利用表达式类型的优势(如上面的 request: Request 参数的例子),请安装@types/express 包。

请求对象代表了 HTTP 请求,并有请求查询字符串、参数、HTTP 头和正文的属性(在此内容)。在大多数情况下,没有必要手动抓取这些属性。我们可以使用专门的装饰器来代替,比如@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 平台(例如ExpressFastify)的类型兼容,Nest 提供了@Res()@Response()装饰器。@Res()@Response() 的简单别名。两者都直接暴露了底层的本地平台响应对象接口。当使用它们时,你也应该导入底层库的类型(例如,@types/express)以充分利用。请注意,当你在方法处理程序中注入@Res()@Response()时,你将 Nest 放入该处理程序的库特定模式中,并且你将负责管理响应。当这样做时,你必须通过调用响应对象(如 res.json(...)res.send(...))来发出某种响应,否则 HTTP 服务器会挂起。

如何定义自己的装饰器,请学习这个章节

资源

早些时候,我们定义了一个方法来获取cats的资源(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 方法提供装饰器。 @Get(), @Post(), @Put(), @Delete(), @Patch(), @Options(), 和 @Head()。此外,@All()定义了一个可以处理所有这些方法的端点。

路由通配符

基于路由的模式也被支持。例如,*被用作通配符,将匹配任何字符的组合。

@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';
}

@nestjs/common包导入HttpCode

通常,你的状态代码不是静态的,而是取决于各种因素。在这种情况下,你可以使用一个库特定的响应(使用@Res()注入)对象(或者,在出现错误时,抛出一个异常)。

头信息

要指定一个自定义的响应头,你可以使用@Header()装饰器或一个库特定的响应对象(并直接调用res.header())。

@Post()
@Header('Cache-Control', 'none')
create() {return 'This action adds a new cat';
}

@nestjs/common包导入Header

重定向

要将一个响应重定向到一个特定的 URL,你可以使用@Redirect()装饰器或一个库特定的响应对象(并直接调用res.redirect())。
@Redirect()需要两个参数,urlstatusCode,都是可选的。如果省略的话,statusCode的默认值是302

@Get()
@Redirect('https://nestjs.com', 301)

有时你可能想动态地确定 HTTP 状态代码或重定向 URL。通过从路由处理方法中返回一个对象来做到这一点,类似如下。

{"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 的猫)。为了定义带参数的路由,我们可以在路由的路径中添加路由参数令牌,以捕获请求 URL 中该位置的动态值。下面@Get()装饰器例子中的路由参数令牌展示了这种用法。以这种方式声明的路由参数可以使用@Param()装饰器进行访问,它应该被添加到方法签名中。

@Get(':id')
findOne(@Param() params): string {console.log(params.id);return `This action returns a #${params.id} cat`;
}

@Param()被用来装饰一个方法参数(上面例子中的params),并使路由参数作为该方法主体中被装饰的方法参数的属性可用。正如上面的代码所见,我们可以通过引用params.id来访问id参数。你也可以向装饰器传递一个特定的参数标记,然后在方法主体中直接引用路由参数的名称。

@nestjs/common包导入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 缺乏对嵌套路由器的支持,当使用子域路由时,应该使用(默认)Express 适配器来代替

与路由路径类似,hosts选项可以使用令牌来捕获主机名称中该位置的动态值。下面@Controller()装饰器例子中的主机参数令牌展示了这种用法。以这种方式声明的主机参数可以使用@HostParam()装饰器进行访问,它应该被添加到方法签名中。

@Controller({ host: ":account.example.com" })
export class AccountController {@Get()getInfo(@HostParam("account") account: string) {return account;}
}

范畴

对于来自不同编程语言背景的人来说,要知道在 Nest 中,几乎所有的东西都是在传入的请求中共享的,这可能是意想不到的。我们有一个到数据库的连接池,有全局状态的单体服务,等等。请记住,Node.js 并不遵循请求/响应的多线程无状态模型,其中每个请求都由一个单独的线程来处理。因此,使用单体实例对我们的应用程序是完全安全的。

但是,在极端情况下,基于请求的控制器生存周祁可能是所需的行为(However, there are edge-cases when request-based lifetime of the controller may be the desired behavior),例如 GraphQL 应用程序中的每个请求缓存,请求跟踪或多租户。在这里了解如何控制作用域

异步性

我们热爱现代 JavaScript,我们知道数据提取大多是异步的。这就是为什么 Nest 支持并能很好地使用异步函数。

学习async / await请点击这里

每个异步函数都必须返回一个 Promise。这意味着你可以返回一个延迟值,Nest 将能够自己解决。让我们来看看这个例子。

@Get()
async findAll(): Promise<any[]> {return [];
}

上述代码是完全有效的。此外,Nest 路由处理程序通过能够返回 RxJS 可观察流而变得更加强大。Nest 将自动订阅下面的源并获取最后一个发出的值(一旦流完成)–( 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 路由处理器的例子没有接受任何客户端参数。让我们通过在这里添加@Body()装饰器来解决这个问题。

但首先(如果你使用 TypeScript),我们需要确定 DTO(数据传输对象)模式。DTO 是一个定义了数据如何在网络上发送的对象。我们可以通过使用 TypeScript 接口,或通过简单的类来确定 DTO 模式。有趣的是,我们在这里推荐使用类。为什么呢?类是 JavaScript ES6 标准的一部分,因此它们在编译后的 JavaScript 中被保留为真实的实体。另一方面,由于 TypeScript 接口在转译过程中被移除,Nest 不能在运行时引用它们。这一点很重要,因为像管道这样的功能在运行时可以访问变量的元类型,从而实现更多的可能性。

我们来创建CreateCatDto类。

export class CreateCatDto {name: string;age: number;breed: string;
}

它只有三个基本属性。此后我们可以在CatsController中使用新创建的DTO

我们的 Validation Pipe 可以过滤掉那些不应该被方法处理程序接收的属性。在这种情况下,我们可以将可接受的属性列入白名单,任何不包括在白名单中的属性都会自动从结果对象中剥离。在CreateCatDto的例子中,我们的白名单是名称、年龄和品种属性在这里了解更多。

处理错误

这里有一个关于处理错误的单独章节(即,与异常一起工作)。

完整例子

下面是一个例子,利用几个可用的装饰器来创建一个基本的控制器。这个控制器暴露了一些方法来访问和操作内部数据。

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 提供了一个自动生成器(示意图),自动生成所有的模板代码,以帮助我们避免做这些事情,并使开发人员的体验更加简单。在这里关于这个功能的信息

开始运行

随着上述控制器的完全定义,Nest 仍然不知道CatsController的存在,因此不会创建这个类的实例。

控制器总是属于一个模块,这就是为什么我们在@Module()装饰器中包含控制器数组。由于我们还没有定义任何其他模块,除了根AppModule,我们将用它来介绍CatsController

import { Module } from "@nestjs/common";
import { CatsController } from "./cats/cats.controller";@Module({controllers: [CatsController],
})
export class AppModule {}

我们使用@Module()装饰器将元数据附加到模块类,Nest 现在可以很容易地反映出哪些控制器必须被安装。

库的特定方法

到目前为止,我们已经讨论了操作响应的 Nest 标准方式。操作响应的第二种方式是使用库特定的响应对象。为了注入一个特定的响应对象,我们需要使用 @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([]);}
}

虽然这种方法是可行的,而且事实上通过提供对响应对象的完全控制,在某些方面确实有更大的灵活性(头文件的操作、库的特定功能等等),但是应该谨慎使用。一般来说,这种方法不那么明确,而且确实有一些缺点。主要的缺点是,你的代码变得依赖于平台(因为底层库在响应对象上可能有不同的 API),而且更难测试(你必须模拟响应对象,等等)。

另外,在上面的例子中,你失去了与依赖 Nest 标准响应处理的 Nest 功能的兼容性,如拦截器和@HttpCode() / @Header() 装饰器。为了解决这个问题,你可以将 passthrough 选项设置为 true,如下所示。

@Get()
findAll(@Res({ passthrough: true }) res: Response) {res.status(HttpStatus.OK);return [];
}

现在,你可以与本地响应对象进行交互(例如,根据某些条件设置 cookies 或头信息),但把其余的事情留给框架。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.luyixian.cn/news_show_283922.aspx

如若内容造成侵权/违法违规/事实不符,请联系dt猫网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【游戏逆向】加密坐标浅析

这个游戏里面坐标有很多种存放方式。 例如明文存放的DOUBLE&#xff0c;加密的各种类型。 我们不知道哪一个对于我们是有用的,哪一些只是辅助UI或则掉到LUA虚拟机坑里的数据。 那就根据作用大小来决定,一一尝试吧。 最好去找修改之后有效果的地址&#xff0c;当然只是本地&…

MySQL中count(1)和count(*)哪个性能好?

当我们对某一张表中的数据需要统计数量的时候&#xff0c;我们通常会用到count(1)、count(*)或者count(字段)&#xff0c;而这三种哪个方式的count效率最高呢&#xff1f;先来说结论&#xff1a; count(1) count(*) > count(字段) 为什么会得到如上的结论&#xff0c;下面来…

1672_MIT 6.828 xv6中如何通过构建环境让系统中增加一个可执行调用文件

全部学习汇总&#xff1a; GreyZhang/g_unix: some basic learning about unix operating system. (github.com) 前面已经分析了如何实现一个系统调用&#xff0c;这个过程的梳理也已经整理成了一份学习笔记。这一次看一下&#xff0c;如何让OS的系统中增加这样的一个可执行的文…

Arduino2.0.4的安装以及上传错误:exit status2

一、安装并下载Arduino 可以进入到下面这个网站中下载会比较快。 Arduino IDE下载&#xff08;9月15日更新到2.0&#xff09;-Arduino爱好者 - Powered by Discuz! Arduino IDE下载&#xff08;9月15日更新到2.0&#xff09;-Arduino爱好者 - Powered by Discuz!Arduino IDE下…

【基于冗余缩减变换:Pan-Sharpening】

Pan-Sharpening Based on Transformer With Redundancy Reduction &#xff08;基于冗余缩减变换的全色锐化算法&#xff09; 基于深度神经网络&#xff08;DNN&#xff09;的泛锐化方法已经产生了最先进的结果。然而&#xff0c;在全色&#xff08;PAN&#xff09;图像和低空…

Python-Python基本用法(全:含基本语法、用户交互、流程控制、数据类型、函数、面向对象、读写文件、异常、断言等)

1 环境准备 编辑器&#xff1a;Welcome to Python.org 解释器&#xff1a;pycharm&#xff1a;Thank you for downloading PyCharm! (jetbrains.com) 2 Quick start 创建项目 new project create demo print(Dad!!)3 基本语法 3.1 print 直接打印 print(Dad!!)拼接打印…

AD20添加元件3D库

Altium Designer是画PCB常用的工具之一,为了PCB的美观性,我们可以采用3D的方式查看已经画好的PCB板。但在这之前需要准备好每个元器件的3D模型。 1、下载3D格式模型 http://www.3dcontentcentral.cn 当然要先注册账户。 在搜索栏输入你想要找的器件。 模型格式STEP AP214…

vue-cli 初始化工程

个人记录下vue-cli创建项目的步骤 卸载老版本的vue-cli (这不是必要的) npm uninstall vue-cli -g 如果本地使用 yarn的话,还需执行 yarn global remove vue-cli 安装全新的vue-cli npm install -g vue/cli 安装指定版本的vue-cli npm install -g vue/…

linux 安装git并拉取代码教程

#一步一步执行以下命令sudo apt install git #安装gitgit --version #查看安装版本号git config user.name jtr #设置用户名git config user.email jiangtrcloudskysec.com #设置邮箱ssh-keygen -t rsa -C "jiangtrcloudskysec.com" #生成秘钥&#xff0c;一直往下按…

自动化测试框架:DrissionPage(1)——安装与设置

发现了一款基于Python的网页自动化工具&#xff1a;DrissionPage。这款工具既能控制浏览器&#xff0c;也能收发数据包&#xff0c;甚至能把两者合而为一&#xff0c;简单来说&#xff1a;集合了WEB浏览器自动化的便利性和 requests 的高效率优点。 一、DrissionPage框架产生背…

【C++进阶】01:概述

概述 OVERVIEW概述C11新特性&#xff1a;C14新特性&#xff1a;C17新特性&#xff1a;C20新特性&#xff1a;C程序编译过程C内存模型CSTL1.Queue&Stack2.String3.MapC语言C语言面向过程编程✅✅面向对象编程&#xff08;类和对象&#xff09;❌✅泛型编程、元编程&#xff…

数据结构之七大排序

数据结构之七大排序&#x1f506;排序的概念及其运用排序的概念常见的排序算法&#x1f506;插入排序直接插入排序希尔排序&#x1f506;选择排序直接选择排序堆排序&#x1f506;交换排序冒泡排序快排&#x1f506;归并排序&#x1f506;非比较排序&#x1f506;结语&#x1f…

十二、Pytorch复现Residual Block

一、Residual Network 论文出处&#xff1a;Deep Residual Learning for Image Recognition 其核心模块&#xff1a; 二、复现Residual Block 这里以两层卷积层为例进行设计复现 resnet可以很好的解决梯度消失问题 Residual Block大致要点&#xff1a; 样本x传入模型&…

下一个“AI王炸”,别只盯着OpenAI,DeepMind也在憋大招

过去几个月&#xff0c;OpenAI风头无两&#xff0c;各大科技公司争先恐后地跟进大语言模型&#xff08;LLM&#xff09;这一技术路线。对比之下&#xff0c;OpenAI的老对手DeepMind&#xff0c;显得有些低调和沉默。微软靠OpenAI打了一场胜仗&#xff0c;而谷歌推出的Bard翻了车…

5.5G,运营商能接受吗?

2月份&#xff0c;我在巴塞罗那参加MWC 2023时有个发现。欧洲通信设备商大多在宣传和讨论6G相关技术&#xff0c;中国通信设备商却在重点展示5.5G&#xff0c;或者叫做5G-Advanced。而全球各国的运营商则都普遍处在比较观望的状态里&#xff0c;两头考察&#xff0c;多样化尝试…

中间表示- 到达定义分析

基本概念 定义&#xff08;def&#xff09;&#xff1a;对变量的赋值 使用&#xff08;use&#xff09;&#xff1a;对变量值的读取 问题&#xff1a;能把上图中的y替换为3吗&#xff1f;如果能&#xff0c;这称之为“常量传播”优化。 该问题等价于&#xff0c;有哪些对变量y…

OPNET Modeler 例程——创建一个移动无线网络

文章目录一、例程概述二、创建天线模型三、创建指向处理器四、创建节点模型1.发射机节点模型2.干扰发射机节点模型3.收信机节点模型五、创建网络模型六、收集统计量并运行仿真七、查看仿真结果总结一、例程概述 OPNET 无线模块支持地面和卫星无线系统的构建。在此例程中将构建…

【C++】基础篇

C基础篇什么是C命名空间命名空间的三种使用方式C的输入和输出缺省参数缺省参数分类函数重载引用引用的使用场景常引用指针和引用的区别auto关键字auto使用细则auto不能推导的场景基于范围的for循环范围for的使用条件指针空值nullptr什么是C 1982年&#xff0c;Bjarne Stroustr…

微服务+springcloud+springcloud alibaba学习笔记【Eureka服务注册中心】(3/9)

Eureka服务注册中心 3/91、服务注册与发现1.1 什么是服务治理&#xff1a;1.2 什么是服务注册与发现&#xff1a;1.3 Eureka服务注册与发现2、单机版eureka2.1 创建module2.2改pom依赖2.3写yml配置文件:2.4主启动类2.5 修改服务提供者 cloud-provider-payment8001 模块&#xf…

GFS的卷类型与集群实验文档

GlusterFS 支持七种卷&#xff0c;即分布式卷、条带卷、复制卷、分布式条带卷、分布式复制卷、条带复制卷和分布式条带复制卷。我们常用的有前五种&#xff0c;今天我们就来看一看这五种卷都有什么优缺点。 一、分布式卷&#xff08;Distribute volume&#xff09; 文件通过 H…