跳到主要内容

字段中间件

注意

本章仅适用于代码优先方式。

字段中间件允许您在字段解析之前或之后运行任意代码。 字段中间件可用于转换字段的结果、验证字段的参数, 甚至检查字段级别的角色(例如,执行中间件函数所需的目标字段的访问权限)。

您可以连接多个中间件函数到一个字段。在这种情况下,它们将按顺序沿着链调用, 其中上一个中间件决定是否调用下一个中间件。middleware数组中中间件函数的顺序很重要。 第一个解析器是“最外层”层,因此它首先执行, 也是最后一个执行的(类似于 graphql-middleware 包)。 第二个解析器是“第二外层”层,因此它第二执行,倒数第二执行。

入门

让我们首先创建一个简单的中间件,它将在将字段值发送回客户端之前将其记录到日志中:

import { FieldMiddleware, MiddlewareContext, NextFn } from '@nestjs/graphql';

const loggerMiddleware: FieldMiddleware = async (
ctx: MiddlewareContext,
next: NextFn,
) => {
const value = await next();
console.log(value);
return value;
};
备注

MiddlewareContext 是一个对象, 由 GraphQL 解析器函数正常接收的相同参数组成({ source,args,context,info }), 而 NextFn 是一个函数,让您执行堆栈中的下一个中间件(绑定到此字段)或实际字段解析器。

注意

字段中间件函数无法注入依赖项,也无法访问 Nest 的 DI 容器,因为它们被设计为非常轻量级, 不应执行任何可能耗时的操作(如从数据库检索数据)。 如果需要调用外部服务/查询数据源的数据,请在绑定到根查询/变异处理程序的守卫/拦截器中执行, 并将其分配给您可以从字段中间件中访问的context对象(具体来说,是 MiddlewareContext 对象)。

请注意,字段中间件必须匹配 FieldMiddleware 接口。 在上面的示例中,我们首先运行 next() 函数(它执行实际的字段解析器并返回字段值), 然后我们将这个值记录到终端。此外,从中间件函数返回的值完全覆盖以前的值, 由于我们不想执行任何更改,因此我们简单地返回原始值。

有了这个,我们可以直接在 @Field() 装饰器中注册我们的中间件,如下所示:

@ObjectType()
export class Recipe {
@Field({ middleware: [loggerMiddleware] })
title: string;
}

现在,每当我们请求 Recipe 对象类型的 title 字段时,原始字段值将被记录到控制台中。

备注

要了解如何使用扩展功能实现字段级别的权限系统, 请查看本节

注意

字段中间件仅可应用于 ObjectType 类。有关更多详细信息, 请查看此问题

另外,正如上面提到的,我们可以在中间件函数中从中控制字段的值。 为了演示目的,让我们将食谱的标题大写(如果存在):

const value = await next();
return value?.toUpperCase();

在这种情况下,每个标题在请求时都将自动大写。

同样,您可以将字段中间件绑定到自定义字段解析器(使用 @ResolveField() 装饰器注释的方法), 如下所示:

@ResolveField(() => String, { middleware: [loggerMiddleware] })
title() {
return 'Placeholder';
}
注意

如果在字段解析器级别启用了增强器(阅读更多), 则字段中间件函数将在绑定到该方法的任何拦截器、守卫等之前运行。

全局字段中间件

除了直接将中间件绑定到特定字段外,您还可以全局注册一个或多个中间件函数。 在这种情况下,它们将自动连接到对象类型的所有字段。

GraphQLModule.forRoot({
autoSchemaFile: 'schema.gql',
buildSchemaOptions: {
fieldMiddleware: [loggerMiddleware],
},
}),
备注

全局注册的字段中间件函数将在本地注册的中间件函数之前执行(直接绑定到特定字段)。