Привет, ребята, с нетерпением жду предстоящего выпуска Apollo Client 2.0! Я пытаюсь перейти на бета-версию, и у меня возникли трудности с настройкой моего промежуточного программного обеспечения для аутентификации. Мой токен авторизации хранится в моем приложении RN через AsyncStorage, который (как вы уже догадались) является асинхронным. Раньше это не было проблемой, так как это был промежуточный код, который я использовал для внедрения токена аутентификации в заголовок:
const authMiddleware = {
async applyMiddleware(req, next) {
if (!req.options.headers)
// Create the header object if needed.
req.options.headers = {};
// Inject the auth header if it exists
const authExists = await Storage.authToken.exists();
if (authExists) {
const token = await Storage.authToken.get();
req.options.headers['Authorization'] = `Bearer ${token}`;
}
next();
}
};
Теперь я переключился на новую реализацию Link, как описано здесь :
const authLink = new ApolloLink(async (operation, forward) => {
const authExists = await Storage.authToken.exists();
if (authExists) {
const token = await Storage.authToken.get();
operation.setContext({
headers: {
Authorization: `Bearer ${token}`
}
});
}
return forward(operation);
});
Единственная проблема заключается в том, что новой библиотеке не нравится ключевое слово async
. Когда я удаляю его и комментирую все, кроме оператора return, я получаю (как и следовало ожидать) ошибку 403 с моего сервера. Однако с добавлением ключевого слова я получаю:
Network error: undefined is not a function (near '..._this.inFlightRequestObservables[key].subscribe...')
Я предполагаю, что это связано с тем фактом, что объект запроса возвращает обещание вместо ожидаемого значения. Есть ли какой-нибудь подход, который мог бы исправить это?
Предполагаемый результат:
Приложение должно применять промежуточное ПО, несмотря на использование промисов.
Фактический результат:
Вышеупомянутая ошибка возникает, если присутствует ключевое слово async
... даже без него... вам все равно нужно вернуть вызов forward()
внутри метода then()
, который не работает.
Как воспроизвести проблему:
Я считаю, что добавление async
к функции запроса любого вызова конструктора ApolloLink сделает это.
Итак, приложив достаточно усилий, я смог сам придумать исправление:
const authLink = new ApolloLink((operation, forward) => {
return new Observable(observable => {
let sub = null;
Storage.authToken.exists().then(exists => {
if (exists) {
Storage.authToken.get().then(token => {
operation.setContext({
headers: {
Authorization: `Bearer ${token}`
}
});
sub = forward(operation).subscribe(observable);
});
} else {
sub = forward(operation).subscribe(observable);
}
});
return () => (sub ? sub.unsubscribe() : null);
});
});
Тем не менее, я очень новичок в этой библиотеке, поэтому я надеюсь, что моя реализация не откроет двери для каких-либо уязвимостей. Я думаю, что, поскольку sub
ссылаются, а не копируют, значение null
будет преобразовано в правильную подписку после разрешения промиса. В любом случае, пока закрываю. Надеюсь, этот код кому-то поможет.
Для тех, кто ищет, apollo-link-context
теперь вещь:
Однако я получил ту же ошибку без async
на constructor
, но async
на Observable
constructor
. Вы можете воспроизвести его следующим образом:
new ApolloLink(() => {
return new Observable(async observer => {
await new Promise((resolve, reject) => setTimeout(resolve, 1000))
observer.next({data: {}})
observer.complete()
})
})
Или самый минимальный шаг для репликации:
new ApolloLink(() => {
return new Observable(() => {
return new Promise()
})
})
Очень странно, как если бы я разворачивал async
/ await
следующим образом:
new ApolloLink(() => {
return new Observable(observer => {
const timeoutPromise = new Promise((resolve, reject) => setTimeout(resolve, 1000))
timeoutPromise.then(() => {
observer.next({data: {}})
observer.complete()
})
})
})
Тогда все хорошо.
Кажется, что библиотека Observable
не любит AsyncFunction
s (которые по сути такие же, как возвращенные Promise
)? Я бы не догадался об этом, поскольку не должно иметь значения, что возвращает constructor
, если observer.{next/complete/error/etc}
вызывается в пределах constructor
соответственно.
Дайте мне знать, если вам потребуется дополнительная информация. Рад открыть отдельную тему, чтобы отслеживать и это.
Спасибо!
Самый полезный комментарий
Для тех, кто ищет,
apollo-link-context
теперь вещь:https://github.com/apollographql/apollo-link/blob/5fbaaf47e5ff1189d65ee674f09fbda464c23d5a/packages/apollo-link-context/README.md