test2
介绍
- 版本node >= 10.13.0:
node -v
- 安装 NestJS cli:
npm i -g @nestjs/cli
nest/cli
- 安装:
npm install -g @nestjs/cli
- 查看版本:
nest --version
- 可视化使用nestcli可以做的所有的事情:
nest --help
- 运行:
npm run start
- 开发中使用:
npm run start:dev
,他会在每次文件更改的时候为我们提供实时编译和自动服务器重建 - 生成模板:
nest g resource 名字
- 安装:
概述
第一步
创建nest项目并运行:
npm i -g @nestjs/cli
nest new project-name
cd project-name
npm i
npm start
**提示:**要创建一个具有 TypeScript 更严格功能集合的新项目,请在
nest new
命令中加上--strict
标志。
将创建 project-name
目录,并安装 node modules 和其他一些样板文件,然后创建一个 src/
目录,并增加几个核心文件:
src
|——app.controller.spec.ts
|——app.controller.ts
|——app.module.ts
|——app.service.ts
|——main.ts
以下是这些核心文件的简要概述:
文件 | 描述 |
---|---|
app.controller.ts | 一个具有单一路由的基本控制器 |
app.controller.spec.ts | 控制器的单元测试. |
app.module.ts | 应用程序的根模块. |
app.service.ts | 一个基本的服务,拥有一个单一的方法. |
main.ts | 应用程序的入口文件将使用核心函数 NestFactory 来创建一个 Nest 应用程序实例. |
main.ts
文件包含一个异步函数,用于引导我们的应用程序:
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
await app.listen(3000);
}
bootstrap();
为了创建一个 Nest 应用程序实例,我们使用核心的 NestFactory
类。NestFactory
暴露了一些静态方法,可以用来创建应用程序实例。create()
方法返回一个实现 INestApplication
接口的应用程序对象。该对象提供了一组方法,后面的章节将对其进行描述。在上面的 main.ts
示例中,我们只是启动了我们的 HTTP
监听器,使应用程序能够处理传入的 HTTP
请求。
控制器controller
控制器负责处理传入的请求并向客户端返回响应
控制器的目的是接收应用程序的特定请求。路由机制控制哪个控制器接收哪些请求。通常,每个控制器都有多个路由,不同的路由可以执行不同的操作
为了创建一个基本的控制器,我们使用类和装饰器。装饰器将类与所需的元数据关联起来,并使Nest能够创建路由映射(将请求与相应的控制器关联起来)
提示:要使用CLI创建控制器,只需执行
nest g controller [name]
命令
路由
在下面的示例中,我们将使用@Controller()
装饰器,这是必需的,用于定义一个基本的控制器。
我们将指定一个可选的路由路径前缀cats
。在@Controller()
装饰器中使用路径前缀可以方便地将一组相关的路由分组,并减少重复的代码。例如,我们可以选择将一组管理与猫实体的交互的路由分组到路径/cats
下。在这种情况下,我们可以在@Controller()
装饰器中指定路径前缀cats
,这样我们就不必在文件中的每个路由中重复该路径的一部分。
// cats.controller.js
import { Controller, Get } from '@nestjs/common';
@Controller('cats')
export class CatsController {
@Get()
findAll() {
return 'This action returns all cats';
}
}
在findAll()
方法前的@Get()
HTTP请求方法装饰器告诉Nest为特定的HTTP请求创建一个处理程序。端点对应于HTTP请求方法(在本例中为GET)和路由路径。什么是路由路径?处理程序的路由路径由连接控制器声明的(可选)前缀和方法装饰器中指定的任何路径决定。由于我们为每个路由声明了前缀(cats
),并且在装饰器中没有添加任何路径信息,Nest会将GET /cats
请求映射到此处理程序。如前所述,路径包括可选的控制器路径前缀和请求方法装饰器中声明的任何路径字符串 。例如,具有前缀路径cats
和装饰器@Get('breed')
将为像GET /cats/breed
这样的请求生成路由映射。
在上面的示例中,当对此端点发出GET请求时,Nest将请求路由到我们定义的findAll()
方法。请注意,我们在此处选择的方法名是完全任意的。我们显然必须声明一个方法来绑定路由,但是Nest不会将选择的方法名附加到任何意义上。
请求对象
处理程序通常需要访问客户端的请求详细信息。Nest提供了对底层平台的请求对象(默认为Express)的访问。我们可以通过在处理程序的签名中添加@Req()
装饰器来指示Nest注入请求对象,从而访问请求对象。
// cats.controller.ts
import { Controller, Get, Req } from '@nestjs/common';
import { Request } from 'express';
@Controller('cats')
export class CatsController {
@Get()
findAll(@Req() request: Request): string {
console.log(request)// 我们可以拿到客户端的信息
return 'This action returns all cats';
}
}
提示:为了利用
express
的类型定义(就像上面的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 |
资源
前面,我们定义了一个用于获取猫资源的端点(GET路由)。通常,我们还希望提供一个用于创建新记录的端点。为此,让 我们创建一个POST处理程序:
// cats.controller.ts
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()
定义了一个处理所有方法的端点。
路由通配符
模式匹配的路由也是支持的。例如,星号(asterisk)用作通配符,将匹配任何字符组合。
@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
。
头部信息
要指定自定义的响应头,你可以使用@Header()
装饰器,也可以使用特定于库的响应对象(并直接调用res.header()
)。
@Post()
@Header('Cache-Control', 'none')
create() {
return 'This action adds a new cat';
}
提示:从
@nestjs/common
包中导入Header
。
重定向
要将响应重定向到特定的URL,你可以使用@Redirect()
装饰器,也可以使用特定于库的响应对象(并直接调用res.redirect()
)。
@Redirect()
接受两个参数,url
和statusCode
,两者都是可选的。如果省略statusCode
,其默认值为302
(Found
)。
@Get()
@Redirect('https://nestjs.com', 301)
有时你可能希望动态确定HTTP状态码或重定向的URL。你可以通过从路由处理程序方法返回具有以下结构的对象来实现:
{
"url": string,
"statusCode": number
}
返回的值将覆盖传递给@Redirect()
装饰器的任何参数。例如:
@Get('docs')
@Redirect('http://nestjs.inode.club', 302)
getDocs(@Query('version') version) {
if (version && version === '5') {
return { url: 'http://nestjs.inode.club/v5/' };
}
}
路由参数
当你需要接受请求的一部分作为动态数据时(例如,GET /cats/1
来获取id为1
的猫),具有静态路径的路由将不起作用。为了定义具有参数的路由,我们可以在路由的路径中添加路由参数标记,以在请求URL中的该位置捕获动态值。在下面的@Get()
装饰器示例中,演示了路由参数标记的用法。以这种方式声明的路由参数可以使用@Param()
装饰器访问,该装饰器应该添加到方法签名中。
提示:具有参数的路由应该在任何静态路径之后声明。这可以防止参数化路径截取前往静态路径的流量。
@Get(':id')
findOne(@Param() params: any): string {
console.log(params.id);
return `This action returns a #${params.id} cat`;
}
提示:从
@nestjs/common
包中导入Param
。
@Get(':id')
findOne(@Param('id') id: string): string {
return `This action returns a #${id} cat`;
}
异步性
我们热爱现代JavaScript,也知道数据提取主要是异步的。这就是为什么Nest支持并且与async
函数很好地配合使用。
每个异步函数都必须返回一个Promise
。这意味着你可以返回一个延迟的值,Nest将能够自行解析它。让我们看一个例子:
// cats.controller.ts
@Get()
async findAll(): Promise<any[]> {
return [];
}
上面的代码是完全有效的。此外,Nest路由处理程序甚至更加强大,因为它们能够返回RxJS observable流。Nest将自动订阅底层的源,并获取最后发出的值(一旦流程完成)。
请求参数
我们之前的POST路由处理程序没有接受任何客户端参数。让我们通过在此处添加@Body()
装饰器来修复这个问题。
但首先(如果你使用TypeScript),我们需要确定DTO(数据传输对象)模式。DTO是一个定义了数据将如何通过网络发送的对象。我们可以使用TypeScript接口或简单类来确定DTO模式。有趣的是,我们在这里建议使用类。为什么呢?类是JavaScript ES6标准的一部分,因此它们在编译后的JavaScript中被保留为实际实体。另一方面,由于TypeScript接口在 编译过程中被删除,Nest无法在运行时引用它们。这很重要,因为诸如Pipes之类的特性在运行时具有变量的元类型时会有额外的可能性。
让我们创建CreateCatDto
类:
// dto/create-cat.dto.ts
export class CreateCatDto {
name: string;
age: number;
breed: string;
}
它只有三个基本属性。然后,我们可以在CatsController
内部使用新创建的DTO:
// cats.controller.ts
@Post()
async create(@Body() createCatDto: CreateCatDto) {
console.log(createCatDto)
return 'This action adds a new cat'
}
提示:我们的
ValidationPipe
可以过滤掉不应由方法处理程序接收的属性。在这种情况下,我们可以将可接受的属性列入白名单,白名单中未包含的任何属性将自动从结果对象中剥离。在CreateCatDto
示例中,我们的白名单是name
、age
和breed
属性。
启动和运行
在上面完全定义了控制器后,Nest仍然不知道CatsController
存在,因此不会创建此类的实例。
控制器始终属于一个模块,这就是为什么我们在@Module()
装饰器中包含了controllers
数组。由于我们还没有定义除了根AppModule
之外的其他模块,所以我们将使用它来引入CatsController
:
// app.module.ts
import { Module } from '@nestjs/common';
import { CatsController } from './cats/cats.controller';
@Module({
controllers: [CatsController],
})
export class AppModule {}
我们使用@Module()
装饰器将元数据附加到模块类,并且Nest现在可以轻松地反映出哪些控制器必须被挂载。
提供者service
控制器应该处理HTTP请求并将更复杂的任务委托给提供者。提供者是普通的JavaScript类,在模块中声明为providers
。
提示:要使用CLI创建控制器,只需执行
nest generate service [name]
命令
服务
让我们首先创建一个简单的CatsService
。这个服务将负责数据的存储和检索,旨在供CatsController
使用,因此它是一个很好的提供者候选对象。
// cats.service.ts
import { Injectable } from '@nestjs/common';
import { Cat } from './interfaces/cat.interface';
@Injectable()
export class CatsService {
private readonly cats: Cat[] = [];
create(cat: Cat) {
this.cats.push(cat);
}
findAll(): Cat[] {
return this.cats;
}
}
我们的CatsService
是一个 基本的类,有一个属性和两个方法。唯一的新功能是它使用了@Injectable()
装饰器。@Injectable()
装饰器附加了元数据,声明CatsService
是一个可以由Nest IoC容器管理的类。顺便说一下,这个示例还使用了一个Cat
接口,可能看起来是这样的:
// interfaces/cat.interface.ts
export interface Cat {
name: string;
age: number;
breed: string;
}
现在我们有了一个用于检索猫的服务类,让我们在CatsController
内部使用它:
// cats.controller.ts
import { Controller, Get, Post, Body } from '@nestjs/common';
import { CreateCatDto } from './dto/create-cat.dto';
import { CatsService } from './cats.service';
import { Cat } from './interfaces/cat.interface';
@Controller('cats')
export class CatsController {
constructor(private catsService: CatsService) {}
@Post()
async create(@Body() createCatDto: CreateCatDto) {
this.catsService.create(createCatDto);
}
@Get()
async findAll(): Promise<Cat[]> {
return this.catsService.findAll();
}
}
CatsService
是通过类构造函数注入的。请注意使用了private
语法。这种简写允许我们在同一位置立即声明和初始化catsService
成员。
供者注册
现在我们已经定义了一个提供者(CatsService
),并且有一个使用该服务的消费者(CatsController
),我们需要将该服务注册到Nest中,以便进行注入。我们可以通过编辑模块文件(app.module.ts
)并 将服务添加到@Module()
装饰器的providers
数组中来实现这一点。
// app.module.ts
import { Module } from '@nestjs/common';
import { CatsController } from './cats/cats.controller';
import { CatsService } from './cats/cats.service';
@Module({
controllers: [CatsController],
providers: [CatsService],
})
export class AppModule {}
模块module
提示:要使用命令行界面(CLI)创建一个模块,只需执行
nest g module [name]
命令。
模块是带有@Module()
装饰器的类。@Module()
装饰器提供元数据,Nest使用这些元数据来组织应用程序的结构。
每个应用程序至少有一个模块,即根模块。根模块是Nest用于构建应用程序图的起点,这是Nest用于解析模块和提供者之间关系和依赖关系的内部数据结构。虽然理论上很小的应用程序可能只有根模块,但这并不是典型情况。我们强调模块是一种有效组织组件的方法。因此,对于大多数应用程序,生成的架构将使用多个模块,每个模块封装了一组紧密相关的功能。
@Module()
装饰器接受一个单一的对象作为参数,其属性描述了模块:
providers | 将由 Nest 注入器实例化并且至少可以在该模块中共享的提供程序享。 |
controllers | 此模块中定义的必须实例化的控制器集 |
imports | 导出此模块所需的提供程序的导入模块列表 |
exports | 这个模块提供的providers 子集应该在引入此模块的其他模块中可用。您可以使用提供者本身,也可以只使用其标记(provide 值)。 |
默认情况下,模块 encapsulates 提供程序。 这意味着不可能注入既不直接属于当前模块也不从导入模块导出的提供程序。 因此,你可以将模块中导出的提供程序视为模块的公 共接口或 API。
特性模块
CatsController
和CatsService
属于同一个应用程序领域。由于它们密切相关,将它们移动到一个特性模块中是有意义的。特性模块简单地组织与特定功能相关的代码,保持代码有组织性,并建立清晰的边界。这有助于我们管理复杂性,并根据SOLID原则进行开发,尤其是在应用程序的规模和/或团队规模增长时。
为了演示这一点,我们将创建CatsModule
。
// cats/cats.module.ts
import { Module } from '@nestjs/common';
import { CatsController } from './cats.controller';
import { CatsService } from './cats.service';
@Module({
controllers: [CatsController],
providers: [CatsService],
})
export class CatsModule {}
在上面,我们在cats.module.ts
文件中定义了CatsModule
,并将与该模块相关的所有内容都移动到了cats
目录中。我们还需要做的最后一件事是将此模块导入到根模块(即在app.module.ts
文件中定义的AppModule
)中
// app.module.ts
import { Module } from '@nestjs/common';
import { CatsModule } from './cats/cats.module';
@Module({
imports: [CatsModule],
})
export class AppModule {}
共享模块
在Nest中,默认情况下,模块是单例的,因此您可以在多个模块之间轻松共享任何提供者的同一实例。
每个模块都自动成为一个共享模块。一旦创建,它可以被任何模块重复使用。假设我们想要在几个其他模块之间共享CatsService
的实例。为了实现这一点,我们首先需要通过将其添加到模块的exports
数组中来导出CatsService
提供者,如下所示:
// cats.module.ts
import { Module } from '@nestjs/common';
import { CatsController } from './cats.controller';
import { CatsService } from './cats.service';
@Module({
controllers: [CatsController],
providers: [CatsService],
exports: [CatsService]
})
export class CatsModule {}
现在,任何导入CatsModule
的模块都可以访问CatsService
,并且将与所有其他导入它的模块共享同一实例。
模块重新导出
如上所示,模块可以导出其内部的提供者。此外,它们还可以重新导出它们导入的模块。在下面的示例中,CommonModule
既被导入到CoreModule
中,又从CoreModule
中导出,从而使得其他导入了CoreModule
的模块也能够使用它。
@Module({
imports: [CommonModule],
exports: [CommonModule],
})
export class CoreModule {}
全局模块
如果您必须在每个地方导入相同的模块集合,那可能会变得很繁琐。与Nest不同,Angular中的providers
在全局范围内注册。一旦定义,它们就可以在任何地方使用。然而,Nest将提供者封装在模块范围内。如果不先导入封装模块,您无法在其他地方使用模块的提供者。
当您想要提供一组应该在开箱即用的情况下随处可用的提供者(例如,辅助函数、数据库连接等),可以使用@Global()
装饰器使模块全局化。
import { Module, Global } from '@nestjs/common';
import { CatsController } from './cats.controller';
import { CatsService } from './cats.service';
@Global()
@Module({
controllers: [CatsController],
providers: [CatsService],
exports: [CatsService],
})
export class CatsModule {}
@Global()
装饰器使模块具有全局范围。全局模块应该仅注册一 次,通常由根模块或核心模块完成。在上面的示例中,CatsService
提供者将是无处不在的,希望注入该服务的模块将不需要在其导入数组中导入CatsModule
。
提示:将所有内容都设为全局并不是一个好的设计决策。全局模块的目的是为了减少必要的样板代码。通常情况下,使用
imports
数组是将模块的API提供给消费者的首选方式.
中间件
Middleware是在路由处理程序之前调用的函数。中间件函数可以访问请求和响应对象,以及应用程序的请求-响应周期中的next()
中间件函数。通常,next中间件函数由一个名为next
的变量表示。
Nest中间件默认情况下与express中间件等效。以下来自官方express文档的描述介绍了中间件的功能:
中间件函数可以执行以下任务:
- 执行任何代码。
- 对请求和响应对象进行更改。
- 结束请求-响应周期。
- 调用堆栈中的下一个中间件函数。
- 如果当前中间件函数未结束请求-响应周期,则必须调用
next()
将控制权传递给下一个中间件函数。否则,请求将被搁置。
您可以在函数中或带有@Injectable()
装饰器的类中实现自定义的Nest中间件。类应该实现NestMiddleware
接口,而函数没有任何特殊要求。让我们首先使用类方法来实现一个简单的中间件功能。
// middleware/logger.middleware.ts
import { Injectable, NestMiddleware } from '@nestjs/common';
import { Request, Response, NextFunction } from 'express';
@Injectable()
export class LoggerMiddleware implements NestMiddleware {
use(req: Request, res: Response, next: NextFunction) {
console.log('Request...');
next();
}
}
依赖注入
Nest中间件完全支持依赖注入。与提供者和控制器一样,它们能够在同一模块内注入依赖项。与往常一样,这是通过constructor
来实现的。
应用中间件
在@Module()
装饰器中没有中间件的位置。相反,我们使用模块类的configure()
方法来设置它们。包含中间件的模块必须实现NestModule
接口。让我们在AppModule
级别设置LoggerMiddleware
。
// app.module.ts
import { MiddlewareConsumer, Module, NestModule, RequestMethod } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { OneModule } from './one/one.module';
import { CatsModule } from './cats/cats.module';
import { LoggerMiddleware } from './cats/middleware/logger.middleware';
@Module({
imports: [OneModule, CatsModule],
controllers: [AppController],
providers: [AppService],
})
export class AppModule implements NestModule{
configure(consumer: MiddlewareConsumer) {
consumer.apply(LoggerMiddleware).forRoutes('cats');
}
}
在上面的示例中,我们为之前在CatsController
中定义的/cats
路由处理程序设置了LoggerMiddleware
。我们还可以通过在配置中传递一个包含路由path
和请求method
的对象来进一步限制中间件适用的请求方法。在下面的示例中,请注意我们导入了RequestMethod
枚举以引用所需的请求方法类型
// app.module.ts
import { MiddlewareConsumer, Module, NestModule, RequestMethod } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { OneModule } from './one/one.module';
import { CatsModule } from './cats/cats.module';
import { LoggerMiddleware } from './cats/middleware/logger.middleware';
@Module({
imports: [OneModule, CatsModule],
controllers: [AppController],
providers: [AppService],
})
export class AppModule implements NestModule{
configure(consumer: MiddlewareConsumer) {
consumer.apply(LoggerMiddleware).forRoutes({
path : 'cats',
method: RequestMethod.GET
});
}
}
提示:
configure()
方法可以使用async/await
进行异步化
路由通配符
模式匹配的路由也是受支持的。例如,星号(*
)被用作通配符,它可以匹配任意字符的组合:
forRoutes({ path: 'ab*cd', method: RequestMethod.ALL });
路由路径 'ab*cd'
将匹配 abcd
、ab_cd
、abecd
等等。字符 ?
、+
、*
和 ()
可以在路由路径中使用,它们是正则表达式的子集。连字符(-
)和点号(.
)在基于字符串的路径中被解释为字面量。
中间件使用者
MiddlewareConsumer
是一个辅助类,它提供了几种内置方法来管理中间件。forRoutes()
方法可以接受一个字符串、多个字符串、一个 RouteInfo
对象、一个控制器类,甚至是多个控制器类。在大多数情况下,您可能只需要传递用逗号分隔的控制器列表
// app.module.ts
export class AppModule implements NestModule{
configure(consumer: MiddlewareConsumer) {
consumer.apply(LoggerMiddleware).forRoutes(CatsController);
}
}
排除路由
有时我们希望排除某些路由不受中间件的影响。我们可以使用 exclude()
方法轻松排除特定的路由。该方法可以接受单个字符串、多个字符串或 RouteInfo
对象,用于标识要排除的路由,如下所示:
consumer
.apply(LoggerMiddleware)
.exclude(
{ path: 'cats', method: RequestMethod.GET },
{ path: 'cats', method: RequestMethod.POST },
'cats/(.*)',
)
.forRoutes(CatsController);
通过上面的示例,LoggerMiddleware
将绑定到 CatsController
中定义的所有路由,除了传递给 exclude()
方法的三个路由。
函数中间件
我们一直使用的 LoggerMiddleware
类相当简单。它没有成员,没有额外的方法,也没有依赖关系。我们为什么不直接将它定义为一个简单的函数,而不是一个类呢?事实上,我们是可以这样做的。这种类型的中间件被称为函数式中间件。下面让我们将 LoggerMiddleware
从基于类的中间件转变为函数式中间件,以说明两者之间的差异:
// logger.middleware.ts
import { Request, Response, NextFunction } from 'express';
export function logger(req: Request, res: Response, next: NextFunction) {
console.log(`Request...`);
next();
};
并在 AppModule
中使用它:
// app.module.ts
consumer
.apply(logger)
.forRoutes(CatsController);
提示:当中间件不需要任何依赖时,可以考虑使用更简单的 函数式中间件 替代方案。
全局中间件
如果我们想要一次性将中间件绑定到每个已注册的路由上,我们可以使用 INestApplication
实例提供的 use()
方法:
// main.ts
const app = await NestFactory.create(AppModule);
app.use(logger);
await app.listen(3000);
提示在全局中间件中无法访问 DI 容器。当使用
app.use()
时,您可以使用功能性中间件。或者,您可以使用类中间件,并在AppModule
(或任何其他模块)中使用.forRoutes('*')
进行使用
异常过滤器
Nest框架内置了一个异常处理层,负责处理应用程序中的所有未处理异常。当一个异常没有被应用程序代码处理时,它会被这个异常处理层捕获,然后自动发送一个适当的用户友好响应。
默认情况下,这个操作是由内置的全局异常过滤器执行的,它处理类型为HttpException
(以及其子类)的异常。当一个异常是未识别的(既不是HttpException
,也不是继承自HttpException
的类),内置的异常过滤器会生成以下默认的JSON响应:
{
"statusCode": 500,
"message": "Internal server error"
}
抛出标准异常
Nest提供了一个内置的HttpException
类,可以从@nestjs/common
包中引入。对于典型的基于HTTP REST/GraphQL的API应用程序,最佳实践是在发生某些错误条件时发送标准的HTTP响应对象。
例如,在CatsController
中,我们有一个findAll()
方法(一个GET
路由处理程序)。假设这个路由处理程序由于某种原因抛出了异常。为了演示这一点,我们可以将其硬编码如下:
// cats.controller.ts
@Get()
async findAll() {
throw new HttpException('Forbidden', HttpStatus.FORBIDDEN);
}
提示:这里我们使用了
HttpStatus
。这是从@nestjs/common