当我的第一个请求返回 401 时,我正在尝试设置刷新令牌策略以使用 GraphQL 和 apollo 客户端刷新 Angular 9 中的 JWT。
我已经为 graphql 设置了一个新的 angular 模块,我正在创建我的 apolloclient。即使使用经过身份验证的请求,一切都很好,但我需要让我的正常刷新令牌策略也能正常工作(刷新令牌周期完成后重新制作并返回原始请求)。我只找到了一些资源来帮助解决这个问题,而且我已经非常接近了——我唯一缺少的是从我的刷新令牌 observable 中返回 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 订阅中的 forward(operation) observable 中注销结果,我可以在最初的 401 失败后看到结果。
if (message.toLowerCase() === 'unauthorized') {
authenticationService.refreshToken().subscribe(() => {
return forward(operation).subscribe(result => {
console.log(result);
});
});
}
上面显示了来自原始请求的数据,但它没有传递给我最初称为 graphql 的组件。
我远非可观察的专家,但我认为我需要做某种地图(平面图、合并图等)以使此返回正常工作,但我只是不知道。
任何帮助将不胜感激
TIA
编辑#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 错误类型错误:您在预期流的位置提供了无效对象。您可以提供 Observable、Promise、Array 或 Iterable。
编辑#3 这是最终的工作解决方案,以防其他人遇到此问题并需要它用于角度。我不喜欢更新我的服务方法来返回一个承诺,然后将该承诺转换为一个 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 { }