refre sh token для angular с использованием клиента graphql и apollo - PullRequest
1 голос
/ 09 мая 2020

Я пытаюсь настроить стратегию токена refre sh для refre sh JWT в angular 9 с GraphQL и клиентом apollo, когда мой первый запрос возвращает 401.

Я установил создаю новый модуль angular для graphql, в котором я создаю свой apolloclient. Все отлично работает даже с аутентифицированными запросами, но мне нужно, чтобы моя обычная стратегия токена refre sh работала также (переделайте и верните исходный запрос после завершения цикла токена refre sh). Я нашел только несколько ресурсов, чтобы помочь с этим, и я подошел очень близко - единственное, что мне не хватает, это возвращение наблюдаемого из моего refre sh token observable.

Вот код, который мог бы подумать, что должно работать:

    import { NgModule } from '@angular/core';
import { HttpLinkModule, HttpLink } from 'apollo-angular-link-http';
import { AuthenticationService } from './authentication/services/authentication.service';
import { ApolloLink } from 'apollo-link';
import { InMemoryCache } from 'apollo-cache-inmemory';
import { ApolloModule, APOLLO_OPTIONS } from 'apollo-angular';
import { onError } from 'apollo-link-error';

export function createApollo(httpLink: HttpLink, authenticationService: AuthenticationService) {

  const authLink = new ApolloLink((operation, forward) => {
    operation.setContext({
      headers: {
        Authorization: 'Bearer ' + localStorage.getItem('auth_token')
      }
    });
    return forward(operation);
  });

  const errorLink = onError(({ forward, graphQLErrors, networkError, operation }) => {
    if (graphQLErrors) {
      graphQLErrors.map(({ message, locations, path }) =>
        {
         if (message.toLowerCase() === 'unauthorized') {
          authenticationService.refreshToken().subscribe(() => {
            return forward(operation);
          });
         }
        }
      );
    }
  });

  return {
    link: errorLink.concat(authLink.concat(httpLink.create({ uri: 'http://localhost:3000/graphql' }))),
    cache: new InMemoryCache(),
  };
}


@NgModule({
  exports: [ApolloModule, HttpLinkModule],
  providers: [
    {
      provide: APOLLO_OPTIONS,
      useFactory: createApollo,
      deps: [HttpLink, AuthenticationService]
    }
  ]
})
export class GraphqlModule { }

Я знаю, что мой запрос работает во второй раз, потому что, если я выйду из системы результата пересылки (операции), наблюдаемой внутри моей подписки на AuthenticationService, я могу увидеть результаты после исходный сбой 401.

 if (message.toLowerCase() === 'unauthorized') {
  authenticationService.refreshToken().subscribe(() => {
    return forward(operation).subscribe(result => {
      console.log(result);
    });
  });
 }

выше показаны данные из исходного запроса, но они не передаются моему компоненту, который изначально вызывал graphql.

Я далек от эксперт по наблюдаемым, но я думаю, что мне нужно сделать какую-то карту (flatmap, mergemap et c), чтобы этот возврат работал правильно, но я просто не знаю.

Любая помощь будет буду очень признателен

TIA

EDIT # 1: это приближает меня, поскольку теперь он фактически подписывается на мой метод в AuthenticationService (Я вижу результаты в tap ())

    const errorLink = onError(({ forward, graphQLErrors, networkError, operation }) => {
    if (graphQLErrors) {
      if (graphQLErrors[0].message.toLowerCase() === 'unauthorized') {
        return authenticationService.refreshToken()
        .pipe(
          switchMap(() => forward(operation))
        );
      }
    }
  });

Теперь я вижу, что выдается эта ошибка:

core. js: 6210 ERROR TypeError: вы указали недопустимый объект, в котором ожидался поток. Вы можете предоставить Observable, Promise, Array или Iterable.

EDIT # 2: Включая снимок экрана с сигнатурой функции onError (): enter image description here

РЕДАКТИРОВАТЬ # 3 Вот окончательное рабочее решение на случай, если кто-то столкнется с этим и понадобится для angular. Мне не нравится обновлять свой метод обслуживания, чтобы вернуть обещание, а затем преобразовывать это обещание в Observable, но, как обнаружил для меня @Andrei Gătej, этот Observable находится из другого пространства имен.

import { NgModule } from '@angular/core';
import { HttpLinkModule, HttpLink } from 'apollo-angular-link-http';
import { AuthenticationService } from './authentication/services/authentication.service';
import { ApolloLink } from 'apollo-link';
import { InMemoryCache } from 'apollo-cache-inmemory';
import { ApolloModule, APOLLO_OPTIONS } from 'apollo-angular';
import { onError } from 'apollo-link-error';
import { Observable } from 'apollo-link';


export function createApollo(httpLink: HttpLink, authenticationService: AuthenticationService) {

  const authLink = new ApolloLink((operation, forward) => {
    operation.setContext({
      headers: {
        Authorization: 'Bearer ' + localStorage.getItem('auth_token')
      }
    });
    return forward(operation);
  });

  const errorLink = onError(({ forward, graphQLErrors, networkError, operation }) => {
    if (graphQLErrors) {
      if (graphQLErrors.some(x => x.message.toLowerCase() === 'unauthorized')) {
        return promiseToObservable(authenticationService.refreshToken().toPromise()).flatMap(() => forward(operation));
      }
    }
  });

  return {
    link: errorLink.concat(authLink.concat(httpLink.create({ uri: '/graphql' }))),
    cache: new InMemoryCache(),
  };
}

const promiseToObservable = (promise: Promise<any>) =>
    new Observable((subscriber: any) => {
      promise.then(
        value => {
          if (subscriber.closed) {
            return;
          }
          subscriber.next(value);
          subscriber.complete();
        },
        err => subscriber.error(err)
      );
    });


@NgModule({
  exports: [ApolloModule, HttpLinkModule],
  providers: [
    {
      provide: APOLLO_OPTIONS,
      useFactory: createApollo,
      deps: [HttpLink, AuthenticationService]
    }
  ]
})
export class GraphqlModule { }

1 Ответ

2 голосов
/ 09 мая 2020

Я не совсем знаком с GraphQL, но думаю, что это должно работать нормально:

if (message.toLowerCase() === 'unauthorized') {
return authenticationService.refreshToken()
  .pipe(
    switchMap(() => forward(operation))
  );
}

Кроме того, если вы хотите знать, как работают mergeMapconcatMap) , вы можете посмотреть этот ответ .

switchMap сохраняет только одну активную внутреннюю наблюдаемую, и как только появится внешнее значение, текущая внутренняя наблюдаемая будет отписана и будет создан новый на основе вновь поступившего внешнего значения и предоставленной функции.

...