这是我的解决方案:
- 您可以传递上下文并对 graphql 订阅(WebSocket)进行身份验证,如下所示:
const server = new ApolloServer({
typeDefs,
resolvers,
context: contextFunction,
introspection: true,
subscriptions: {
onConnect: (
connectionParams: IWebSocketConnectionParams,
webSocket: WebSocket,
connectionContext: ConnectionContext,
) => {
console.log('websocket connect');
console.log('connectionParams: ', connectionParams);
if (connectionParams.token) {
const token: string = validateToken(connectionParams.token);
const userConnector = new UserConnector<IMemoryDB>(memoryDB);
let user: IUser | undefined;
try {
const userType: UserType = UserType[token];
user = userConnector.findUserByUserType(userType);
} catch (error) {
throw error;
}
const context: ISubscriptionContext = {
// pubsub: postgresPubSub,
pubsub,
subscribeUser: user,
userConnector,
locationConnector: new LocationConnector<IMemoryDB>(memoryDB),
};
return context;
}
throw new Error('Missing auth token!');
},
onDisconnect: (webSocket: WebSocket, connectionContext: ConnectionContext) => {
console.log('websocket disconnect');
},
},
});
- 您可以使用解析器中的方法传递解析器的上下文参数,
pubsub.publish
如下所示:
addTemplate: (
__,
{ templateInput },
{ templateConnector, userConnector, requestingUser }: IAppContext,
): Omit<ICommonResponse, 'payload'> | undefined => {
if (userConnector.isAuthrized(requestingUser)) {
const commonResponse: ICommonResponse = templateConnector.add(templateInput);
if (commonResponse.payload) {
const payload = {
data: commonResponse.payload,
context: {
requestingUser,
},
};
templateConnector.publish(payload);
}
return _.omit(commonResponse, 'payload');
}
},
Subscription
现在,我们可以在解析器subscribe
方法中获取 http 请求上下文和订阅(websocket)上下文,如下所示:
Subscription: {
templateAdded: {
resolve: (
payload: ISubscriptionPayload<ITemplate, Pick<IAppContext, 'requestingUser'>>,
args: any,
subscriptionContext: ISubscriptionContext,
info: any,
): ITemplate => {
return payload.data;
},
subscribe: withFilter(templateIterator, templateFilter),
},
},
async function templateFilter(
payload?: ISubscriptionPayload<ITemplate, Pick<IAppContext, 'requestingUser'>>,
args?: any,
subscriptionContext?: ISubscriptionContext,
info?: any,
): Promise<boolean> {
console.count('templateFilter');
const NOTIFY: boolean = true;
const DONT_NOTIFY: boolean = false;
if (!payload || !subscriptionContext) {
return DONT_NOTIFY;
}
const { userConnector, locationConnector } = subscriptionContext;
const { data: template, context } = payload;
if (!subscriptionContext.subscribeUser || !context.requestingUser) {
return DONT_NOTIFY;
}
let results: IUser[];
try {
results = await Promise.all([
userConnector.findByEmail(subscriptionContext.subscribeUser.email),
userConnector.findByEmail(context.requestingUser.email),
]);
} catch (error) {
console.error(error);
return DONT_NOTIFY;
}
//...
return true;
}
如您所见,现在我们从 HTTP request 获取订阅用户(与 graphql webserver 建立 WebSocket 连接)和 HTTP request user(将突变发送到 graphql webserver subscriptionContext
)context
。
如果函数的返回值是真的,那么你就可以做剩下的工作了templateFilter
,然后WebSocket会推送消息给订阅用户payload.data
,否则,它不会。
此templateFilter
函数将根据订阅用户的数量执行多次,这意味着它是可迭代的。现在您在此函数中获取每个订阅用户,并执行您的业务逻辑来决定是否将 WebSocket 消息推送到订阅用户(客户端)。
请参阅github 示例存储库
文章: