Вопрос:

Проблемы с извлечением JWT из AsyncStorage для создания промежуточного программного обеспечения httpLink с ApolloClient

javascript react-native async-await graphql asyncstorage

376 просмотра

2 ответа

7295 Репутация автора

Я в значительной степени копирую / вставляю пример кода из документов Apollo GraphQL Network Layer по созданию промежуточного программного обеспечения , за исключением того, что я извлекаю свои зависимости apollo-client-presetи использую простую служебную библиотеку для возврата токена jwt. Вот код ссылки ...

import { ApolloClient, HttpLink, InMemoryCache, ApolloLink, concat } from 'apollo-client-preset';
import { getSessionToken } from './api/localStorage';

const httpLink = new HttpLink({ uri: 'http://localhost:4000/' });

const authMiddleware = new ApolloLink((operation, forward) => {
  const token = getSessionToken();
  const authorizationHeader = token ? `Bearer ${token}` : null;

  operation.setContext({
    headers: {
      authorization: authorizationHeader,
    }
  });

  return forward(operation);
})

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

И вот ./api/localStorageметод в игре ...

export const getSessionToken = () => {
    return AsyncStorage.getItem(AUTH_TOKEN);
}

Выполнение приведенного выше кода приводит к HTTP-запросу со следующими заголовками ...

POST / HTTP/1.1
Host: localhost:4000
Content-Type: application/json
User-Agent: Expo/2.2.0.1011489 CFNetwork/893.14 Darwin/17.3.0
Connection: keep-alive
Accept: */*
Accept-Language: en-us
Authorization: Bearer [object Object]
Accept-Encoding: gzip, deflate
Content-Length: 174

Обратите внимание на Authorization: Bearer [object Object]бит

Я думаю, что проблема довольно очевидна. AsyncStorage - это асинхронная функция, и мне нужно дождаться разрешения обещания, прежде чем собирать строку авторизации. Решение, однако, менее очевидно. Я пробовал разные методы, но любви пока нет.

Я видел пару примеров, похожих на выше. Вот еще один из Как в модуле аутентификации GraphQL . Ни один из них не показывает, как дождаться разрешения асинхронного хранилища в первую очередь. Я знаю, что Аполлон иногда ждет от вас автоматических обещаний, поэтому я подумал, что это может быть так. Но, похоже, не в моем опыте.

Так в чем же решение?

Вот пара вещей, которые я пробовал, но которые не удалось.

1. Promise.then ()

const middlewareAuthLink = new ApolloLink((operation, forward) => {
  getSessionToken().then(token => {
    const authorizationHeader = token ? `Bearer ${token}` : null
    operation.setContext({
      headers: {
        authorization: authorizationHeader
      }
    })
  });

  return forward(operation)
})

Это правильно создает строку авторизации, но не создает промежуточное программное обеспечение, потому что это не создает заголовок авторизации. Мне кажется, это лучший выбор, но я, должно быть, делаю это неправильно по причинам, которые не очевидны для меня.

POST / HTTP/1.1
Host: localhost:4000
Content-Type: application/json
Connection: keep-alive
Accept: */*
User-Agent: Expo/2.2.0.1011489 CFNetwork/893.14 Darwin/17.3.0
Content-Length: 174
Accept-Language: en-us
Accept-Encoding: gzip, deflate

Обратите внимание на отсутствие Authorizationзаголовка

2. асинхронный / ожидание

Я думал, что это будет довольно просто, превратить это в асинхронную / ожидающую функцию, но не так сильно. Вот код

const middlewareAuthLink = new ApolloLink(async (operation, forward) => {
  const token = await getSessionToken();
  const authorizationHeader = token ? `Bearer ${token}` : null;

  operation.setContext({
    headers: {
      authorization: authorizationHeader,
    }
  });
  return forward(operation)
})

Но это привело к этой супер уродливой ошибке красного экрана смерти ...

Unhandled (in react-apollo:Apollo(EventList)), ApolloError@http://localhost:19001/node_modules/expo/AppEntry.bundle?platform=ios&dev=true&strict=false&minify=false&hot=false&assetPlugin=/Users/ChrisGeirman/dev/mobile/playground/exp/frogquest-app/node_modules/expo/tools/hashAssetFiles:106405:36
currentResult@http://localhost:19001/node_modules/expo/AppEntry.bundle?platform=ios&dev=true&strict=false&minify=false&hot=false&assetPlugin=/Users/ChrisGeirman/dev/mobile/playground/exp/frogquest-app/node_modules/expo/tools/hashAssetFiles:106516:43
dataForChild@http://localhost:19001/node_modules/expo/AppEntry.bundle?platform=ios&dev=true&strict=false&minify=false&hot=false&assetPlugin=/Users/ChrisGeirman/dev/mobile/playground/exp/frogquest-app/node_modules/expo/tools/hashAssetFiles:101608:79
render@http://localhost:19001/node_modules/expo/AppEntry.bundle?platform=ios&dev=true&strict=false&minify=false&hot=false&assetPlugin=/Users/ChrisGeirman/dev/mobile/playground/exp/frogquest-app/node_modules/expo/tools/hashAssetFiles:101659:49
finishClassComponent@http://localhost:19001/node_modules/expo/AppEntry.bundle?platform=ios&dev=true&strict=false&minify=false&hot=false&assetPlugin=/Users/ChrisGeirman/dev/mobile/playground/exp/frogquest-app/node_modules/expo/tools/hashAssetFiles:4528:102
performUnitOfWork@http://localhost:19001/node_modules/expo/AppEntry.bundle?platform=ios&dev=true&strict=false&minify=false&hot=false&assetPlugin=/Users/ChrisGeirman/dev/mobile/playground/exp/frogquest-app/node_modules/expo/tools/hashAssetFiles:5547:33
workLoop@http://localhost:19001/node_modules/expo/AppEntry.bundle?platform=ios&dev=true&strict=false&minify=false&hot=false&assetPlugin=/Users/ChrisGeirman/dev/mobile/playground/exp/frogquest-app/node_modules/expo/tools/hashAssetFiles:5566:142
_invokeGuardedCallback@http://localhost:19001/node_modules/expo/AppEntry.bundle?platform=ios&dev=true&strict=false&minify=false&hot=false&assetPlugin=/Users/ChrisGeirman/dev/mobile/playground/exp/frogquest-app/node_modules/expo/tools/hashAssetFiles:2707:23
invokeGuardedCallback@http://localhost:19001/node_modules/expo/AppEntry.bundle?platform=ios&dev=true&strict=false&minify=false&hot=false&assetPlugin=/Users/ChrisGeirman/dev/mobile/playground/exp/frogquest-app/node_modules/expo/tools/hashAssetFiles:2681:41
performWork@http://localhost:19001/node_modules/expo/AppEntry.bundle?platform=ios&dev=true&strict=false&minify=false&hot=false&assetPlugin=/Users/ChrisGeirman/dev/mobile/playground/exp/frogquest-app/node_modules/expo/tools/hashAssetFiles:5602:41
scheduleUpdateImpl@http://localhost:19001/node_modules/expo/AppEntry.bundle?platform=ios&dev=true&strict=false&minify=false&hot=false&assetPlugin=/Users/ChrisGeirman/dev/mobile/playground/exp/frogquest-app/node_modules/expo/tools/hashAssetFiles:5723:105
enqueueForceUpdate@http://localhost:19001/node_modules/expo/AppEntry.bundle?platform=ios&dev=true&strict=false&minify=false&hot=false&assetPlugin=/Users/ChrisGeirman/dev/mobile/playground/exp/frogquest-app/node_modules/expo/tools/hashAssetFiles:4341:179
forceUpdate@http://localhost:19001/node_modules/expo/AppEntry.bundle?platform=ios&dev=true&strict=false&minify=false&hot=false&assetPlugin=/Users/ChrisGeirman/dev/mobile/playground/exp/frogquest-app/node_modules/expo/tools/hashAssetFiles:8265:38
forceRenderChildren@http://localhost:19001/node_modules/expo/AppEntry.bundle?platform=ios&dev=true&strict=false&minify=false&hot=false&assetPlugin=/Users/ChrisGeirman/dev/mobile/playground/exp/frogquest-app/node_modules/expo/tools/hashAssetFiles:101579:58
next@http://localhost:19001/node_modules/expo/AppEntry.bundle?platform=ios&dev=true&strict=false&minify=false&hot=false&assetPlugin=/Users/ChrisGeirman/dev/mobile/playground/exp/frogquest-app/node_modules/expo/tools/hashAssetFiles:101554:50
error@http://localhost:19001/node_modules/expo/AppEntry.bundle?platform=ios&dev=true&strict=false&minify=false&hot=false&assetPlugin=/Users/ChrisGeirman/dev/mobile/playground/exp/frogquest-app/node_modules/expo/tools/hashAssetFiles:109797:25
forEach@[native code]
error@http://localhost:19001/node_modules/expo/AppEntry.bundle?platform=ios&dev=true&strict=false&minify=false&hot=false&assetPlugin=/Users/ChrisGeirman/dev/mobile/playground/exp/frogquest-app/node_modules/expo/tools/hashAssetFiles:106747:44
http://localhost:19001/node_modules/expo/AppEntry.bundle?platform=ios&dev=true&strict=false&minify=false&hot=false&assetPlugin=/Users/ChrisGeirman/dev/mobile/playground/exp/frogquest-app/node_modules/expo/tools/hashAssetFiles:107277:47
http://localhost:19001/node_modules/expo/AppEntry.bundle?platform=ios&dev=true&strict=false&minify=false&hot=false&assetPlugin=/Users/ChrisGeirman/dev/mobile/playground/exp/frogquest-app/node_modules/expo/tools/hashAssetFiles:107649:29
forEach@[native code]
http://localhost:19001/node_modules/expo/AppEntry.bundle?platform=ios&dev=true&strict=false&minify=false&hot=false&assetPlugin=/Users/ChrisGeirman/dev/mobile/playground/exp/frogquest-app/node_modules/expo/tools/hashAssetFiles:107648:27
forEach@[native code]
broadcastQueries@http://localhost:19001/node_modules/expo/AppEntry.bundle?platform=ios&dev=true&strict=false&minify=false&hot=false&assetPlugin=/Users/ChrisGeirman/dev/mobile/playground/exp/frogquest-app/node_modules/expo/tools/hashAssetFiles:107644:33
http://localhost:19001/node_modules/expo/AppEntry.bundle?platform=ios&dev=true&strict=false&minify=false&hot=false&assetPlugin=/Users/ChrisGeirman/dev/mobile/playground/exp/frogquest-app/node_modules/expo/tools/hashAssetFiles:107239:51
tryCallOne@http://localhost:19001/node_modules/expo/AppEntry.bundle?platform=ios&dev=true&strict=false&minify=false&hot=false&assetPlugin=/Users/ChrisGeirman/dev/mobile/playground/exp/frogquest-app/node_modules/expo/tools/hashAssetFiles:10901:14
http://localhost:19001/node_modules/expo/AppEntry.bundle?platform=ios&dev=true&strict=false&minify=false&hot=false&assetPlugin=/Users/ChrisGeirman/dev/mobile/playground/exp/frogquest-app/node_modules/expo/tools/hashAssetFiles:10987:25
_callTimer@http://localhost:19001/node_modules/expo/AppEntry.bundle?platform=ios&dev=true&strict=false&minify=false&hot=false&assetPlugin=/Users/ChrisGeirman/dev/mobile/playground/exp/frogquest-app/node_modules/expo/tools/hashAssetFiles:12157:15
_callImmediatesPass@http://localhost:19001/node_modules/expo/AppEntry.bundle?platform=ios&dev=true&strict=false&minify=false&hot=false&assetPlugin=/Users/ChrisGeirman/dev/mobile/playground/exp/frogquest-app/node_modules/expo/tools/hashAssetFiles:12193:17
callImmediates@http://localhost:19001/node_modules/expo/AppEntry.bundle?platform=ios&dev=true&strict=false&minify=false&hot=false&assetPlugin=/Users/ChrisGeirman/dev/mobile/playground/exp/frogquest-app/node_modules/expo/tools/hashAssetFiles:12397:31
__callImmediates@http://localhost:19001/node_modules/expo/AppEntry.bundle?platform=ios&dev=true&strict=false&minify=false&hot=false&assetPlugin=/Users/ChrisGeirman/dev/mobile/playground/exp/frogquest-app/node_modules/expo/tools/hashAssetFiles:2301:30
http://localhost:19001/node_modules/expo/AppEntry.bundle?platform=ios&dev=true&strict=false&minify=false&hot=false&assetPlugin=/Users/ChrisGeirman/dev/mobile/playground/exp/frogquest-app/node_modules/expo/tools/hashAssetFiles:2179:32
__guard@http://localhost:19001/node_modules/expo/AppEntry.bundle?platform=ios&dev=true&strict=false&minify=false&hot=false&assetPlugin=/Users/ChrisGeirman/dev/mobile/playground/exp/frogquest-app/node_modules/expo/tools/hashAssetFiles:2287:11
flushedQueue@http://localhost:19001/node_modules/expo/AppEntry.bundle?platform=ios&dev=true&strict=false&minify=false&hot=false&assetPlugin=/Users/ChrisGeirman/dev/mobile/playground/exp/frogquest-app/node_modules/expo/tools/hashAssetFiles:2178:19
flushedQueue@[native code]
invokeCallbackAndReturnFlushedQueue@[native code]

Так как же дождаться возврата результата AsyncStorage перед установкой заголовка?

Автор: Chris Geirman Источник Размещён: 02.01.2018 03:20

Ответы (2)


6 плюса

7295 Репутация автора

Решение

Могут быть и другие решения или даже лучшие решения, но это, наконец, сработало для меня, поэтому я публикую это как решенное.

import { setContext } from 'apollo-link-context';

const httpLink = new HttpLink({ uri: 'http://localhost:4000/'});

const authHeader = setContext(
  request =>
    new Promise((success, fail) => {
      getSessionToken().then(token => success({ headers: { authorization: `Bearer ${token}` }}))
    })
)

const client = new ApolloClient({
  link: concat(authHeader, httpLink),
  cache: new InMemoryCache(),
})
Автор: Chris Geirman Размещён: 02.01.2018 11:45

0 плюса

2698 Репутация автора

Вот альтернатива, которая загружает токен из кэша в памяти, который должен работать лучше, чем выборка из AsyncStorage. В качестве альтернативы вы можете просто использовать функцию-оболочку для получения, которая кэширует ответ, AsyncStorageно наличие этого как части кэша Apollo имеет дополнительное преимущество повторного рендеринга при обновлении токена.

import gql from 'graphql-tag'
import ApolloClient from 'apollo-client'
import { setContext } from 'apollo-link-context'
import { createHttpLink } from 'apollo-link-http'
import { InMemoryCache } from 'apollo-cache-inmemory'
import { ApolloLink } from 'apollo-link'
import AsyncStorage from '@react-native-community/async-storage'

const authLink = setContext(async (_, { headers }) => {
  const context = await client.query({
    query: gql`
      query AuthQuery {
        authToken @client
      }
    `
  })
  const {
    data: { authToken: token }
  } = context
  let extra = {}
  if (token) {
    extra = { authorization: `Bearer ${token}` }
  }
  return {
    headers: {
      ...headers,
      ...extra
    }
  }
})

const httpLink = createHttpLink({
  uri: 'http://localhost:4000/'
})

const cache = new InMemoryCache()

const client = new ApolloClient({
  link: ApolloLink.from([authLink, httpLink]),
  cache
})

client.writeData({ data: { authToken: null } })

AsyncStorage.getItem('token').then((token: string | null) => {
  if (token) {
    client.writeData({ data: { authToken: token } })
  }
})

export { client }
Автор: david_adler Размещён: 15.05.2019 12:58
Вопросы из категории :
32x32