apolloClient.query 不使用中间件,而 <Query /> 使用

Posted

技术标签:

【中文标题】apolloClient.query 不使用中间件,而 <Query /> 使用【英文标题】:apolloClient.query not using middleware, while <Query /> does 【发布时间】:2019-06-27 12:07:26 【问题描述】:

我有一个带有中间件的 apolloclient,它控制台记录一个不记名令牌,因为我并不总是在我应该进行身份验证的时候进行身份验证。

由于某种原因,来自 react-apollo &lt;Query /&gt; 对象的查询似乎使用了这个中间件——我看到了我的控制台消息——但是我以编程方式触发的查询:apolloClient.query 不记录任何内容(没有代码执行此操作的方式,控制台日志位于 authLink 中间件的顶部)。

在切换到 apolloclient 之前,我使用 apollo-boost 开始了我的项目,所以我认为切换后 node_modules 可能没有正确设置。但是我已经移除并重新安装了 yarn,它现在应该没有任何 apollo-boost 的痕迹。

此外,如果我将用于创建 apolloclient 的代码复制到我的事务中,使其使用本地副本而不是全局副本,则中间件会触发。

即:

export const relayBBNToGraphcool = async () => 
    /* BEGIN without this code, WHICH IS ALREADY in the instantiation of apolloClient, the result is `user: null` */
    const authLink = setContext(async (req,  headers ) => 
        // get the authentication token from local storage if it exists
        let getToken = async () => await AsyncStorage.getItem(/*'access_token'*/'graphcool_token')
        const token =  await getToken()

        console.trace('token for connection to graphcool is currently', token, req.operationName)

        // return the headers to the context so httpLink can read them
        return token
            ? 
                headers: 
                ...headers,
                authorization: token ? `Bearer $token` : null,
                
            
            :  headers 
    )

    const httpLink = new HttpLink(config)
    const link = ApolloLink.from([/* retryLink, */ authLink, httpLink])

    const cache = new InMemoryCache()
    // overriding apolloClient in the global scope of this module
    const apolloClient = new ApolloClient(
        link,
        cache
    )
    /* END */    

    apolloClient.query( query: User.self, forceFetch: true )
        .then(authneticatedUser => 
            console.trace('response', authneticatedUser)

            if(authneticatedUser.data.user === null)
                throw ('no user')

apolloClient 是从 apollo-client 而不是 apollo-boost 配置的。它在 App.js 中附加到它的提供者:

return (
  <ApolloProvider client=this.state.apolloClient>

使用 getApolloClient() 从不同的文件加载——它设置了一个局部变量 apolloClient

var apolloClient //...
export const getApolloClient = () =>  // ...
    apolloClient = new ApolloClient(
        link,
        cache
    ) //...
    return apolloClient

.query.mutate 的所有调用都是从同一文件中的导出函数完成的,并且它们使用相同的 var apolloClient。我从不实例化一个以上的 apollo 客户端。为什么我的一些查询触发了中间件,而其他查询却没有?

编辑:

每个请求,实际使用的链接:

// from src: https://github.com/kadikraman/offline-first-mobile-example/blob/master/app/src/config/getApolloClient.js
export const getApolloClient = async () => 
    const retryLink = new RetryLink(
        delay: 
            initial: 1000
        ,
        attempts: 
            max: 1000,
            retryIf: (error, _operation) => 
                if (error.message === 'Network request failed') 
                    //if (_operation.operationName === 'createPost') 
                    //    return true
                
                return false
            
        
    )

    // from: https://www.apollographql.com/docs/react/recipes/authentication.html
    const authLink = setContext(async (req,  headers ) => 
        // get the authentication token from local storage if it exists
        let getToken = async () => await AsyncStorage.getItem(/*'access_token'*/'graphcool_token')
        const token =  await getToken()

        console.trace('token for connection to graphcool is currently', token, req.operationName)

        // return the headers to the context so httpLink can read them
        return token
            ? 
                headers: 
                ...headers,
                authorization: token ? `Bearer $token` : null,
                
            
            :  headers 
    )

    const httpLink = new HttpLink(config)
    const link = ApolloLink.from([retryLink, authLink, httpLink])

    const cache = new InMemoryCache()

    apolloClient = new ApolloClient(
        link,
        cache
    )

    try 
        await persistCache(
        cache,
        storage: AsyncStorage
        )
     catch (err) 
        console.error('Error restoring Apollo cache', err) // eslint-disable-line no-console
    

    return apolloClient

【问题讨论】:

请包含您正在使用的实际链接的相关代码,包括您的自定义链接。 @DanielRearden 好的,我已经在所有链接中包含了客户端初始化。 【参考方案1】:

原来问题与缓存有关——getApolloClient方法中的这一部分:

try 
    await persistCache(
    cache,
    storage: AsyncStorage
    )
 catch (err) 
    console.error('Error restoring Apollo cache', err) // eslint-disable-line no-console

如果我在将更改应用于发送到 ApolloProvider 的副本之前更改代码以保存 apolloClient,则它可以工作,如下所示:

export var apolloClient

// from src: https://github.com/kadikraman/offline-first-mobile-example/blob/master/app/src/config/getApolloClient.js
export const getApolloClient = async () => 
    apolloClient = await getRawClient()
    try 
        await persistCache(
        cache,
        storage: AsyncStorage
        )
     catch (err) 
        console.error('Error restoring Apollo cache', err) // eslint-disable-line no-console
    

    return apolloClient


export const getRawClient = async () => 
    const retryLink = new RetryLink(
        delay: 
            initial: 1000
        ,
        attempts: 
            max: 1000,
            retryIf: (error, _operation) => 
                if (error.message === 'Network request failed') 
                    //if (_operation.operationName === 'createPost') 
                    //    return true
                
                return false
            
        
    )

    // from: https://www.apollographql.com/docs/react/recipes/authentication.html
    const authLink = setContext(async (req,  headers ) => 
        // get the authentication token from local storage if it exists
        let getToken = async () => await AsyncStorage.getItem(/*'access_token'*/'graphcool_token')
        const token =  await getToken()

        console.trace('token for connection to graphcool is currently', token, req.operationName)

        // return the headers to the context so httpLink can read them
        return token
            ? 
                headers: 
                ...headers,
                authorization: token ? `Bearer $token` : null,
                
            
            :  headers 
    )

    const httpLink = new HttpLink(config)
    const link = ApolloLink.from([/* retryLink, */ authLink, httpLink])

    const cache = new InMemoryCache()

    return new ApolloClient(
        link,
        cache
    )

然后,我还从这个文件中重构查询和变异代码,导入 apolloClient. 行得通……这有点奇怪,但无论如何。

【讨论】:

以上是关于apolloClient.query 不使用中间件,而 <Query /> 使用的主要内容,如果未能解决你的问题,请参考以下文章

如何在 ApolloClient.query() 上使用“缓存和网络”策略

通过 react(ApolloClient.query()) 方法从 GraphQL 服务器获取数据

带参数的阿波罗客户端查询 - Vue

网络错误:req.headers.forEach Apollo Client Angular4

使用 IMiddleware 时添加自定义中间件不起作用

修改 OWIN OAuth 中间件以使用 JWT 不记名令牌