Как обновить заголовок авторизации ApolloClient после успешного входа в систему?

По сути, у нас есть это в нашем файле 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 объект без перезагрузки всего приложения?

Похоже, что документы Apollo Client 3 показывают, как включать заголовок в каждый вызов. apollographql.com/docs/react/networking/authentication/#head‌​er ncabral ниже имеет ответ

eAndy 19.05.2021 16:35
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
8
1
6 712
5
Перейти к ответу Данный вопрос помечен как решенный

Ответы 5

Вам придется использовать ссылку. Ссылка

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),
});

Однако наше приложение использует ApolloClient из apollo-boost и не распознает вариант link.

catandmouse 21.05.2019 09:14

Дэнг, хотелось бы, чтобы это не было заминусовано. С новым клиентом Apollo 3 это предпочтительный путь, который сэкономил бы мне время. Полностью пропустил этот ответ из-за отрицательных голосов >.>

afreeland 23.08.2020 06:19

@afreeland согласился, этот ответ сильно недооценен.

eAndy 19.05.2021 16:32
Ответ принят как подходящий

Вы можете использовать функцию 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(),
      },
    });
  },
});

Если вы используете Apollo Client 3, вы должны проверить ответ ниже от ncabral, который использует setContext Apollo Client. Этот ответ упоминается в последних документах API клиента Apollo (см. ссылку REF в ответе ncabral).

eAndy 19.05.2021 16:31

У меня была аналогичная проблема. Я хотел использовать 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>

Я не уверен, что это лучший способ исправить это. На данный момент это работает, и я надеюсь, что это поможет другим.

Когда я использую это, я получаю сообщение об ошибке Invalid Hook Error. «Хук можно вызывать только внутри тела функционального компонента». что имеет смысл, так как используемая здесь функция не является компонентом.

peteredhead 26.08.2020 04:20

Другой способ добиться этого — использовать метод setLink из apollo-config. После этого экспортируйте функцию, которая установит заголовки, как только у вас появится токен.

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));
}

Надеюсь, это поможет кому-то

Ответ 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;

Другие вопросы по теме