Angular SSR未等待AWS Cognito令牌获取,求合规解决方案
Angular SSR 等待Amplify Auth会话的原生解决方案
针对你遇到的Angular SSR渲染时不等待Amplify Auth会话初始化、返回HTML仅含加载指示器的问题,在Angular 17中可以采用以下两种原生方案替代宏任务hack:
方案1:服务器端预获取令牌并注入应用
核心思路是在Angular应用渲染前,由SSR服务端提前完成Auth会话的获取,将令牌作为注入令牌传递给应用,避免在Apollo链路中异步等待:
- 定义注入令牌
import { InjectionToken } from '@angular/core'; export const SSR_AUTH_TOKEN = new InjectionToken<string | undefined>('SSR_AUTH_TOKEN');
- 修改
server.ts的请求处理逻辑
app.get('*', async (req, res) => { let authToken: string | undefined; try { // 配置SSR端的Amplify Auth存储 const ssrAuthConfig = { ...yourAmplifyConfig, storage: new UniversalStorage({ req }) }; Auth.configure(ssrAuthConfig); // 预获取会话令牌 const session = await Auth.currentSession(); authToken = session.getAccessToken().getJwtToken(); } catch (err) { authToken = undefined; } // 渲染应用时注入令牌 const html = await renderApplication(AppComponent, { document: indexHtml, url: req.originalUrl, platformProviders: [ { provide: REQUEST, useValue: req }, { provide: SSR_AUTH_TOKEN, useValue: authToken } ] }); res.send(html); });
- 更新Apollo客户端配置
export function createApollo( platformId: Object, @Optional() ssrAuthToken: string | undefined ) { return { cache, link: ApolloLink.from([ setContext(async () => { // SSR端直接使用预注入的令牌 if (!isPlatformBrowser(platformId) && ssrAuthToken) { return authHeaders(ssrAuthToken); } // 客户端保留原有异步获取逻辑 try { const session = await Auth.currentSession(); return authHeaders(session.getAccessToken().getJwtToken()); } catch { return unAuthHeaders(); } }), httpLink.create({ uri: environment.graphqlEndpoint }) ]) }; } // 在app.config.ts中注册Apollo配置 export const appConfig: ApplicationConfig = { providers: [ { provide: APOLLO_OPTIONS, useFactory: createApollo, deps: [PLATFORM_ID, [new Optional(), SSR_AUTH_TOKEN]] } ] };
方案2:使用APP_INITIALIZER全局初始化Auth会话
通过Angular的APP_INITIALIZER钩子,强制SSR在应用渲染前完成Auth会话初始化:
- 创建Auth会话服务
@Injectable({ providedIn: 'root' }) export class AuthSessionService { private token: string | undefined; constructor( @Inject(PLATFORM_ID) private platformId: Object, @Optional() @Inject(REQUEST) private request: Request ) {} async initSession(): Promise<void> { if (isPlatformBrowser(this.platformId)) return; // SSR端配置Auth并获取令牌 const ssrAuthConfig = { ...yourAmplifyConfig, storage: new UniversalStorage({ req: this.request }) }; Auth.configure(ssrAuthConfig); try { const session = await Auth.currentSession(); this.token = session.getAccessToken().getJwtToken(); } catch { this.token = undefined; } } getToken(): string | undefined { return this.token; } }
- 在
app.config.ts中注册初始化钩子
export const appConfig: ApplicationConfig = { providers: [ { provide: APP_INITIALIZER, useFactory: (authSessionService: AuthSessionService) => () => authSessionService.initSession(), deps: [AuthSessionService], multi: true } ] };
- 修改Apollo配置使用预初始化令牌
export function createApollo(authSessionService: AuthSessionService) { return { cache, link: ApolloLink.from([ setContext(async () => { const token = authSessionService.getToken(); if (token) { return authHeaders(token); } // 客户端逻辑不变 try { const session = await Auth.currentSession(); return authHeaders(session.getAccessToken().getJwtToken()); } catch { return unAuthHeaders(); } }), httpLink.create({ uri: environment.graphqlEndpoint }) ]) }; }
原NgZone方案无效的原因
Amplify Auth的异步操作可能脱离了Angular Zone的上下文,导致Angular无法追踪该宏任务。上述两种方案要么将异步操作移至应用渲染前,要么通过Angular原生初始化钩子强制等待,均无需依赖hack手段。
内容的提问来源于stack exchange,提问作者Strojmistr
相关产品推荐
相关产品推荐

