GraphQL 订阅返回 null 数据。

3

我的问题是,在我的React应用程序中,每当创建订单时,我希望获取该订单的订阅,名称为orderNotification。

在订单解析器中进行设置:

  Subscription: {
        orderNotification: {
            subscribe: (_, __, { pubsub }) => pubsub.asyncIterator(ORDER_NOTIFICATION)
        }
    }

突变:

Mutation: {
        async createOrder(_, { MakeOrderInput: { state, message, products, total } }, context) {

            try {
                const userAuth = isAuth(context);
                const pubsub = context.pubsub;

                const newOrder = new Order({
                    state,
                    username: userAuth.username,
                    user: userAuth.id,
                    createdAt: new Date().toISOString(),
                    total,
                    message,
                    products,
                    userAddress: userAuth.address,
                });

           
                const index = products.findIndex(x => x.cost === 0);

                if (index != -1) {
                    const u = await User.findById({ _id: userAuth.id });
                    await User.findByIdAndUpdate({ _id: userAuth.id }, { points: u.points - 20 }, (err, data) => {
                        if (err) {
                            console.log(err)
                        } else {
                            console.log('fatto')
                        }
                    });
                }

                const order = await newOrder.save();

                pubsub.publish(ORDER_NOTIFICATION, {
                    orderNotification: order
                });

                return order;

            } catch (err) {
                // throw new Error(err);
                console.log(err)
            }
        },

在GraphQL Playground中一切都正常,但当我需要获取并显示组件中的结果时,返回的数据为空:

import React from 'react'
import gql from 'graphql-tag';
import { useSubscription } from '@apollo/client';

import { Box } from 'grommet'

function SubscriptionOrder() {

    const { data, loading, error } = useSubscription(SUBSCRIPTION_USER_ORDER, {
        onSubscriptionData: (d) => console.log(d),
        onSubscriptionComplete: (da) => console.log(da)
    });

    // return null
    // console.log(data)

    return (
        <>
            <Box style={{ marginTop: '96px' }}>
                {data && data.orderNotification ? (
                    <h1>hi: {data.orderNotification.username}</h1>
                ) : (
                        <h1>NO DATA</h1>
                    )
                }
            </Box>
        </>
    )

};

const SUBSCRIPTION_USER_ORDER = gql`
  subscription orderNotification{
    orderNotification {
        username
    }
  }
`;

export default SubscriptionOrder;

考虑到在playground中运行时出现错误,可能是由于我的ApolloClient链接配置存在问题:

import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import { ApolloClient } from 'apollo-client';
import { ApolloProvider } from '@apollo/react-hooks';
import { InMemoryCache } from 'apollo-cache-inmemory';
import { HttpLink } from 'apollo-link-http';
import { onError } from 'apollo-link-error';
import { ApolloLink, Observable, split } from 'apollo-link';

import { WebSocketLink } from 'apollo-link-ws';
import { getMainDefinition } from 'apollo-utilities';

import { TokenRefreshLink } from "apollo-link-token-refresh";
import jwtDecode from "jwt-decode";

import { getAccessToken, setAccessToken } from './accessToken';

import dotenv from 'dotenv/config.js'

const cache = new InMemoryCache({});

const httpLink = new HttpLink({
  uri: process.env.NODE_ENV === 'development' ? `${process.env.REACT_APP_SERVER_DEV}/graphql` : `${process.env.REACT_APP_SERVER_PRODUCTION}/graphql`,
  credentials: "include",
});

const wsLink = new WebSocketLink({
  uri: process.env.NODE_ENV === 'development' ? `ws://${process.env.REACT_APP_SERVER_DEV_WS}/graphql` : `ws://${process.env.REACT_APP_SERVER_PRODUCTION_WS}/graphql`,
  options: {
    reconnect: true,
    lazy: true,
    inactivityTimeout: 1000,
  },
  connectionCallback: err => {
    if (err) {
      console.log('Error Connecting to Subscriptions Server', err);
    }
  }
});


const splitLink = split(
  ({ query }) => {
    const { kind, operation } = getMainDefinition(query);
    return kind === "OperationDefinition" && operation === "subscriptions";
  },
  wsLink,
  httpLink
);

const requestLink = new ApolloLink(
  (operation, forward) =>
    new Observable(observer => {
      let handle;
      Promise.resolve(operation)
        .then(operation => {
          const accessToken = getAccessToken();
          if (accessToken) {
            operation.setContext({
              headers: {
                authorization: `Bearer ${accessToken}`
              },
              fetchOptions: {
                credentials: 'include'
              }
            });
          }
        })
        .then(() => {
          handle = forward(operation).subscribe({
            next: observer.next.bind(observer),
            error: observer.error.bind(observer),
            complete: observer.complete.bind(observer)
          });
        })
        .catch(observer.error.bind(observer));

      return () => {
        if (handle) handle.unsubscribe();
      };
    })
);

const client = new ApolloClient({
  link: ApolloLink.from([
    new TokenRefreshLink({
      accessTokenField: "accessToken",
      isTokenValidOrUndefined: () => {
        const token = getAccessToken();

        if (!token) {
          return true;
        }

        try {
          const { exp } = jwtDecode(token);
          if (Date.now() >= exp * 1000) {
            return false;
          } else {
            return true;
          }
        } catch {
          return false;
        }
      },
      fetchAccessToken: () => {
        return fetch(process.env.NODE_ENV === 'development' ? `${process.env.REACT_APP_SERVER_DEV}/refresh_token` : `${process.env.REACT_APP_SERVER_PRODUCTION}/refresh_token`, {
          method: "POST",
          credentials: "include"
        });
      },
      handleFetch: accessToken => {
        setAccessToken(accessToken);
      },
      handleError: err => {
        console.warn("Your refresh token is invalid. Try to relogin");
        console.error(err);
      }
    }),
    onError(({ graphQLErrors, networkError }) => {
      console.log(graphQLErrors);
      console.log(networkError);
    }),
    requestLink,
    splitLink,
  ]),
  cache,
  connectToDevTools: true,
  credentials: 'include',
});



ReactDOM.render(
  <React.StrictMode>
    <ApolloProvider client={client}>
      <App />
    </ApolloProvider>
  </React.StrictMode >,
  document.getElementById('root')
);

这是我的服务器:

import { PubSub } from 'graphql-subscriptions';
const pubsub = new PubSub();

const server = new ApolloServer({
    typeDefs,
    resolvers,
    context: ({ req, res }) => ({ req, res, pubsub }),
    introspection: true,
    cors: corsOptions,
});

server.applyMiddleware({ app, cors: false });

const httpServer = http.createServer(app);
server.installSubscriptionHandlers(httpServer);

httpServer.listen(PORT, () => {
    console.log(` Server ready at http://localhost:${PORT}${server.graphqlPath}`)
    console.log(` Subscriptions ready at ws://localhost:${PORT}${server.subscriptionsPath}`)
})

不看代码...你在网络选项卡中看到了什么?检查地址/端口/协议/头部信息。 - xadm
没有经过身份验证的用户?抛出错误而不仅仅记录日志?现在你的代码即使没有用户也会继续执行。 - xadm
是的,当然有授权用户。 - Mirko Albanese
问题在于订阅只在 playground 中起作用,useSubscription 钩子只在组件挂载时运行,而且每当我创建新订单时,该钩子不会运行。 - Mirko Albanese
没有原因,组件中没有数据发生变化...比较 playground 和应用程序的网络通信,找出差异(例如错误的 URL/目标)和原因...例如 operation === "subscriptionsssssssssssss" - xadm
显示剩余4条评论
2个回答

6
解决订阅中的有效载荷问题。

 Subscription: {
        orderNotification: {
            subscribe: (_, __, { pubsub }) => pubsub.asyncIterator(ORDER_NOTIFICATION),
             resolve: (payload) => {
                   return payload;
             },
        }
    }


1
这对我有用,但如果我们不操作有效载荷,为什么需要 resolve 呢?顺便说一下,在这种情况下,我认为你需要 return payload.orderNotification - Michael Lynch

0

另一种去除空值或不需要的条目的方法是使用withFilter,以便在出现未知或不需要的事件时消除提示;

将此答案作为一个选项,但上面的答案更好。

const { withFilter } = require('graphql-subscriptions');

 Subscription: {
        orderNotification: {
            subscribe: withFilter(
                () => pubsub.asyncIterator('ORDER_NOTIFICATION'),
                (payload, variables) => {
                    // add any condition here
                    return (payload && payload !== null);
                },
            ),
        },
    }


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接