错误处理
nest
内置的异常层负责处理整个应用程序中的所有抛出的异常。当捕获到未处理的异常时,最终用户将收到友好的响应。
开箱即用,此操作由内置的全局异常过滤器执行,该过滤器处理类型
HttpException
(及其子类)的异常。每个发生的异常都
由全局异常过滤器处理
,
当这个异常无法被识别时
(
既不是
HttpException
也不是继承的类
HttpException ) ,
用户将
收到以下
JSON
响应
:
{"statusCode": 500,"message": "Internal server error"}
同时,我们也可以根据需要进行异常处理

基础异常类
Nest提供了一个内置的 HttpException 类,它从 @nestjs/common 包中导入。
现在当客户端调用这个端点时,响应如下所示:
{ "statusCode": 403, "message": "Forbidden" }
HttpException
构造函数有两个必要的参数来决定响应
:
response
参数定义
JSON
响应体。它可以是
string
或
object
,如下所述。
status
参数定义
HTTP
状态代码。
默认情况下,
JSON
响应主体包含两个属性:
statusCode
:默认为
status
参数中提供的
HTTP
状态代码
message:
基于状态的
HTTP
错误的简短描述
仅覆盖
JSON
响应主体的消息部分,请在
response
参数中提供一个
string
。
要覆盖整个
JSON
响应主体,请在
response
参数中传递一个
object
。
Nest
将序列化对象,并将其作为
JSON
响应返回。
第二个构造函数参数
-status-
是有效的
HTTP
状态代码

使用上面的代码,响应如下所示:

自定义异常
如果确实需要创建自定义的异常,则最好创建自己的异常层次结构,其中自定义异常从基
HttpException
类继承。 使用这种方法,
Nest
可以识别我
们定义的异常,并自动处理错误响应。

由于
ForbiddenException
扩展了基础
HttpException
,它将和核心异常处理程序一起工作,因此我们可以在
findAll()
方法中使用它。

在许多情况下,我们无需编写自定义异常,而可以使用内置的
Nest HTTP
异常
内置HTTP异常
为了减少样板代码,
Nest
提供了一系列继承自核心异常
HttpException
的可用异常,所有这些都可以在
@nestjs/common
包中找到:
BadRequestException
请求错误
UnauthorizedException
未授权
NotFoundException
请求路径未找到
ForbiddenException
用户无权限访问该资源,请求失败
NotAcceptableException
RequestTimeoutException
请求超时
ConflictException
GoneException
PayloadTooLargeException
UnsupportedMediaTypeException
UnprocessableException
请求被服务器正确解析,但是包含无效字段
InternalServerErrorException
服务器发生错误
NotImplementedException
服务没有实现请求方式
BadGatewayException
ServiceUnavailableException
GatewayTimeoutException
异常过滤器
虽然基本(内置)异常过滤器可以为您自动处理许多情况,但有时您可能希望对异常层拥有完全控制权,例如,您可能要添加日志记录或基于一
些动态因素使用其他
JSON
模式。 异常过滤器正是为此目的而设计的。 它们使您可以控制精确的控制流以及将响应的内容发送回客户端。
创建一个异常过滤器,它负责捕获作为
HttpException
类实例的异常,并为它们设置自定义响应逻辑。为此,我们需要访问底层平台
Request
和
Response
。我们将访问
Request
对象,以便提取原始
url
并将其包含在日志信息中。我们将使用
Response.json()
方法,使用
Response
对象直接控制发
送的响应。
所有异常过滤器都应该实现通用的
ExceptionFilter<T>
接口。它需要你使用有效签名提供
catch(exception: T, host: ArgumentsHost)
方法。
T
表示异常的
类型。

@Catch()
装饰器绑定所需的元数据到异常过滤器上。它告诉
Nest
这个特定的过滤器正在寻找
HttpException
而不是其他的。在实践中,
@Catch()
可以传递多个参数,所以你可以通过逗号分隔来为多个类型的异常设置过滤器
让我们将
HttpExceptionFilter
绑定到
CatsController
的
create()
方法上。

在这里使用了
@UseFilters()
装饰器。和
@Catch()
装饰器类似,它可以使用单个过滤器实例,也可以使用逗号分隔的过滤器实例列表。 我们
创建了
HttpExceptionFilter
的实例。另一种可用的方式是传递类(不是实例),让框架承担实例化责任并启用依赖注入。
尽可能使用类而不是实例。由于
Nest
可以轻松地在整个模块中重复使用同一类的实例,因此可以减少内存使用。

在上面的示例中,
HttpExceptionFilter
仅应用于单个
create()
路由处理程序,使其成为方法范围的。 异常过滤器的作用域可以划分为不同的
级别:方法范围,控制器范围或全局范围。

要创建一个全局范围的过滤器,您需要执行以下操作

捕获异常
为了捕获每一个未处理的异常(不管异常类型如何),将 @Catch() 装饰器的参数列表设为空,例如 @Catch()。
要创建一个全局范围的过滤器,您需要执行以下操作

Providers(提供者)
Providers
是
Nest
的一个基本概念。许多基本的
Nest
类可能被视为
provider - service, repository, factory, helper
等等。 他们都可以通过
constructor
注入依赖关系。 这意味着对象可以彼此创建各种关系,并且
“
连接
”
对象实例的功能在很大程度上可以委托给
Nest
运行时系统。
Provider
只是一个用
@Injectable()
装饰器注释的类。

控制器应处理
HTTP
请求并将更复杂的任务委托给
providers
。
Providers
是纯粹的
JavaScript
类,在其类声明之前带有
@Injectable()
装饰器。
创建服务
要使用
CLI
创建服务类,只需执行
$ nest g service cats
命令
依赖注入
在
contrlloer
使用我们创建的服务,我们采用是通过类的控制器构造函数进行依赖注入的。
Nest
是建立在强大的设计模式
,
通常称为依赖注入。
在
Nest
中,借助
TypeScript
功能,管理依赖项非常容易,因为它们仅按类型进行解析
模块
模块是具有
@Module()
装饰器的类。
@Module()
装饰器提供了元数据,
Nest
用它来组织应用程序结构。
每个
Nest
应用程序至少有一个模块,即根模块。根模块是
Nest
开始安排应用程序树的地方。事实上,根模块可能是应用程序中唯一的模块,
特别是当应用程序很小时,但是对于大型程序来说这是没有意义的。在大多数情况下,您将拥有多个模块,每个模块都有一组紧密相关的
功能。
@module()
装饰器接受一个描述模块属性的对象:
providers
由
Nest
注入器实例化的提供者,并且可以至少在整个模块中共享
controllers
必须创建的一组控制器
imports
导入模块的列表,这些模块导出了此模块中所需提供者
exports
由本模块提供并应在其他模块中可用的提供者的子集。

中间件
中间件是在路由处理程序 之前 调用的函数。 中间件函数可以访问请求和响应对象,以及应用程序请求响应周期中的
next()
中间件函数。
next()
中间件函数通常由名为
next
的变量表示。
Nest
中间件实际上等价于
express
中间件
中间件函数可以执行以下任务
:
执行任何代码
对请求和响应对象进行更改
结束请求
-
响应周期
调用堆栈中的下一个中间件函数
如果当前的中间件函数没有结束请求
-
响应周期
,
它必须调用
next()
将控制传递给下一个中间件函数。否则
,
请求将被挂起。

创建中间件
可以在函数中或在具有
@Injectable()
装饰器的类中实现自定义
Nest
中间件。 这个类应该实现
NestMiddleware
接口
,
而函数
没有任何特殊的要求。

Nest
中间件完全支持依赖注入。 就像提供者和控制器一样,它们能够注入属于同一模块的依赖项(通过
constructor
)。
应用中间件
中间件不能在
@Module()
装饰器中列出。我们必须使用模块类的
configure()
方法来设置它们。包含中间件的模块必须实现
NestModule
接口

我们还可以在配置中间件限制处理的路径和方法,只需要将路径和方法传递给
forRoutes()
方法,也可以可以使用
async/await
来实现

函数式中间件
我们使用的
LoggerMiddleware
类非常简单。它没有成员,没有额外的方法,没有依赖关系。为什么我们不能只使用一个简
单的函数?这是一个很好的问题,因为事实上
-
我们可以做到。这种类型的中间件称为函数式中间件。让我们把
logger
转
换成函数。


.
当您的中间件没有任何依赖关系时,我们可以考虑使用函数式中间件。
连续中间件
为了绑定顺序执行的多个中间件,我们可以在
apply()
方法内用逗号分隔它们

全局中间件
如果我们想一次性将中间件绑定到每个注册路由,我们可以使用由INestApplication实例提供的 use()方法: