登录成功后如何更新ApolloClient授权头?

问题描述 投票:0回答:6

基本上,我们在

index.js
文件中设置了
ApolloProvider
授权来进行查询/突变。

import React from 'react';
import ReactDOM from 'react-dom';

import App from './App';

import ApolloClient from "apollo-boost";
import { ApolloProvider } from "react-apollo";

let session = localStorage.getItem("session");
let authorization = "";
if(session) {
    let sessionObj = JSON.parse(session);
    authorization = sessionObj.access_token
}

const graphQLServerURL = process.env.REACT_APP_API_URL;
const client = new ApolloClient({
    uri: graphQLServerURL + "/graphql",
    headers: {
       authorization,
    }
});

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

应用程序首次加载时,

authorization
标题将为
null
。然而,在
<App>
组件中,我们有一个
<Login>
组件,它基本上使用
username
password
执行发布请求。在
.then()
方法中成功请求后,我们有:

.then(res => {
if (res === 200) {
    localStorage.setItem("session", JSON.stringify(res.data));
    history.push("/dashboard");
});

因此,用户被重定向到一个

<Dashboard>
组件,该组件有一个
<Query>
组件(用于列出一些数据)。然而,在我点击刷新之前,
authorization
中的
ApolloClient
仍然是
null
。使用
push
不会重新加载
<App>
组件(以便它从本地存储获取更新的
session
)。

我应该如何做到这一点,在登录成功发布请求后,

index.js
的授权可以获得最新的
session
对象,而无需重新加载整个应用程序?

javascript reactjs session local-storage graphql
6个回答
11
投票

如果您使用

request
,则可以使用
apollo-boost

功能
const getToken = () => {
  const token = localStorage.getItem('token');
  return token ? `Bearer ${token}` : '';
};

const client = new ApolloClient({
  uri: `${graphQLServerURL}/graphql`,
  request: (operation) => {
    operation.setContext({
      headers: {
        authorization: getToken(),
      },
    });
  },
});

9
投票

您必须使用链接。 参考

const httpLink = createHttpLink({
  uri: '/graphql',
});

const authLink = setContext((_, { headers }) => {
  // get the authentication token from local storage if it exists
  const token = localStorage.getItem('token');
  // return the headers to the context so httpLink can read them
  return {
    headers: {
      ...headers,
      authorization: token ? `Bearer ${token}` : "",
    }
  }
});

const client = new ApolloClient({
  link: authLink.concat(httpLink),
});

3
投票

我也有类似的问题。我想用

token
变成
ApolloProvider
。当应用程序第一次渲染时,我无法访问
localStorage
,因此 WebSocket 无法工作。

我做了什么?

我创建了一个自定义挂钩并使用

useEffect
useState
来更新它。


import {
  ApolloClient, InMemoryCache, HttpLink, split,
} from '@apollo/client';
import { WebSocketLink } from 'apollo-link-ws';
import { getMainDefinition } from 'apollo-utilities';
import { useEffect, useState } from 'react';
import config from './index';

export const useConfigClient = () => {
  const [authTokenStorage, setAuthTokenStorage] = useState(localStorage.getItem('token'));   // by default it is null

  const cache = new InMemoryCache();

  const httpLink = new HttpLink({
    uri: config.GRAPHQL_URL,
    headers: {
      authorization: `Bearer ${authTokenStorage}`,
    },
  });

  const wsLink = new WebSocketLink({
    uri: config.GRAPHQL_WS,
    options: {
      reconnect: true,
      connectionParams: {
        headers: {
          authorization: `Bearer ${authTokenStorage}`,
        },
      },
    },
  });

  const link = split(
    ({ query }) => {
      const definition = getMainDefinition(query);
      return (
        definition.kind === 'OperationDefinition'
        && definition.operation === 'subscription'
      );
    },
    wsLink,
    httpLink,
  );

  useEffect(() => {
    setAuthTokenStorage(localStorage.getItem('token')); // as soon as it is available just update the token
  }, []);

  const client = new ApolloClient({
    cache,
    link,
  });

  return client;
};

我如何使用自定义钩子?


import { ApolloProvider } from '@apollo/client';
import { useConfigClient } from '../config/apolloConfig';  // import the hooks

<ApolloProvider client={useConfigClient()}>
  //  ... add your component
</ ApolloProvider>

我不确定这是解决问题的最佳方法。目前,它有效,我希望它能帮助其他人。


1
投票

实现此目的的另一种方法是您可以使用 apollo-config 中的

setLink
方法。 之后,导出一个函数,该函数将在您获得令牌后设置标头。

import {ApolloClient, InMemoryCache, HttpLink} from '@apollo/client';
import {setContext} from '@apollo/client/link/context';

const httpLink = new HttpLink({uri: '/graphql'});

let config = new ApolloClient({
  link: httpLink,
  cache: new InMemoryCache(),
});

export function setGraphqlHeaders() {
  const token = localStorage.getItem(AUTH_TOKEN);
  const authLink = setContext((_, {headers}) => {
    return {
      headers: {
        ...headers,
        authorization: token || null,
      },
    };
  });

  config.setLink(authLink.concat(httpLink));
}

希望,这会对某人有所帮助


1
投票

praveen-me
的答案对我在 2021 年使用 Nextjs 有效。我尝试了
ncabral
的答案,但我不确定它会自动为我更新 JWT。

praveen-me
的答案之上,我在代码中添加了一些内容:

userToken.ts

const userTokenKey = 'userToken'

export const getUserToken = () => {
    try {
        return localStorage.getItem(userTokenKey)   
    } catch (error) {
        ...
    }
}

export const setUserToken = (token: string) => {
    try {
        localStorage.setItem(userTokenKey, token)
    } catch (error) {
        ...
    }
}

apolloClient.ts

import { ApolloClient, createHttpLink, InMemoryCache } from "@apollo/client";
import { setContext } from "@apollo/client/link/context";
import { getUserToken } from "./userToken";

const httpLink = createHttpLink({
    uri: process.env.NEXT_PUBLIC_GRAPHQL_URL
})

let client = new ApolloClient({
    // link: authLink.concat(httpLink),
    link: httpLink,
    cache: new InMemoryCache(),
});

export function updateGraphqlHeaders() {
    const authLink = setContext((_, { headers }) => {
        const token = getUserToken();
        return {
            headers: {
                ...headers,
                authorization: token ? `Bearer ${token}` : "",
            }
        }
    })
    client.setLink(authLink.concat(httpLink))
}
updateGraphqlHeaders() // Init

export default client;

_app.tsx

...
import { ApolloProvider } from "@apollo/client";
import client from "../auth/apolloClient";

function MyApp({ Component, pageProps }: AppProps) {
  return (
    <ApolloProvider client={client}>
      <Component {...pageProps} />
    </ApolloProvider>
  );
}

export default MyApp;

0
投票

首先创建layout.tsx

import { ApolloProvider } from "@apollo/client"; 
 
export default function RootLayout({ children }:any) {

    const client = createApolloClient();
     
    return (
    <html lang="en">
      <ApolloProvider client={client}>  
      
      </ApolloProvider>  
    </html>
  )
}

你需要创建apollo-client.js

import { ApolloClient, createHttpLink, InMemoryCache } from '@apollo/client';
import { setContext } from '@apollo/client/link/context';
import { getUserToken } from "./userToken";

const API_URL = process.env.API_URL;

const httpLink = createHttpLink({
  uri: API_URL,
});


const authLink = setContext(async (_, { headers }) => {
    
  // get the authentication token from local storage if it exists  
  const userToken = await getUserToken()  
  const token =  userToken || null;
  
  // return the headers to the context so httpLink can read them
  return {
    headers: {
      ...headers,
      authorization: token ? `Bearer ${token}` : "",
    }
  }
});

const createApolloClient = () => {
    
    let client = '';    
    client = new ApolloClient({
      link: authLink.concat(httpLink),
      cache: new InMemoryCache()
    }); 
    return client  
};

export default createApolloClient; 

创建userToken.js

export const getUserToken = () => { 
    try {
        return JSON.parse(localStorage.getItem('token'));    
    } catch (error) {
        console.log(error)
    }
}

这对我有用,如果有的话请评论

© www.soinside.com 2019 - 2024. All rights reserved.