使用 Angular 关闭浏览器 window / 选项卡时执行异步服务获取

Perform an asynchronous service get when closing browser window / tab with Angular

我目前正在尝试让 Angular 应用程序在用户试图离开我的页面时发出 API 服务调用。通过关闭选项卡 / window,或输入外部 URL.

我已经尝试实施我在此处看到的关于 Whosebug 问题的几种不同方式,但 none 似乎达到了预期的效果。有些人提到同步 ajax 请求,这些请求已经或正在 Chrome 的 onbeforeunload 中被弃用。其他人建议使用 XMLHttpRequests,这似乎遭遇了同样的命运。 我尝试使用 @HostListener('window:onbeforeunload', ['$event']),等同于HTML(window:beforeunload)="doBeforeUnload($event)",但一律不予配合。我可以让 console.logs 出现,但 API 从未收到任何注销用户的电话。 我还使用了 navigator.sendBeacon,尽管它的函数返回 true,表示作业已发送到浏览器队列,但没有任何结果。

目前注销功能是这样的:

userLogout(): Observable<any> {
    return this.apiService.get('/Account/Logout/');
  }

这又是一个通用的 http get 函数:

get<T>(url: string, options?: { params: HttpParams }): Observable<any> {
    return this.httpRequest<T>('get', url, null, options ? options.params : null);
  }

完整的 url 是通过使用 HttpInterceptor 添加的。注销功能本身正在运行,因为它在菜单选项中使用,手动使用时可以按预期工作。所以功能应该不是问题。

我想做的就是以同步或异步方式调用 userLogout 函数,并通知后端用户已离开。

经过一些修改,我最终得到了基于此 answer 的解决方案,尽管出于某种原因,它在我的第一次尝试中并不适用。

我向组件添加了以下 HostBinding:

@HostListener('window:beforeunload', ['$event']) beforeunloadHandler(event) {
    this.pageClosed();
}

它调用一个简单的函数,该函数将使用身份验证服务注销方法。

pageClosed() {
    this.authenticationService.userLogoutSync();
}

我必须创建一个单独的注销函数,这样它才能同步。 Headers 还必须添加后端授权,因为我的 Http 拦截器无法捕获 XMLHttpRequest 并自行添加它们。

const xhr = new XMLHttpRequest();
    xhr.open('GET', environment.backend + '/Account/Logout/', false);
    xhr.setRequestHeader('Authorization', 'Bearer ' + this.jwtService.getAccessToken());
    xhr.send();
}

对于 angular 中的我来说,async/await 在关闭浏览器选项卡/更改选项卡时进行 http 调用效果很好 url。

@HostListener('window:beforeunload', ['$event']) 
async beforeunloadHandler(event) {
    await triggerSyncLogout();
}
async triggerSyncLogout(){
    let url = 'www.sample.com';
    let reqHeaders = new HttpHeaders().append('Authorization', token).append('Accept', 'application/json');
    const options = {
        headers: reqHeaders,
        withCredentials: true,
        observe: 'response' as 'response'
    }
    return await this.http.get<any>(url, options).toPromise();
}