【问题标题】:Angular - How to add an access token from an observable to an outgoing HTTP request?Angular - 如何将可观察的访问令牌添加到传出的 HTTP 请求?
【发布时间】:2019-03-13 10:57:54
【问题描述】:

在我的UserService 中,我有一个用户Observable 对象,其中包含登录用户的UserModel。为了测试,我在ngOnInit() 中实现了登录过程:

this.userService.authenticate('###', '###')
   .subscribe(res => console.log('authenticated'));
private userSource = new BehaviorSubject<UserModel>(null);
   public user = this.userSource.asObservable();

My UserModel 提供了一个名为 authKey 的属性,用于 API 身份验证。

在我的ProjectService 我想做一个 api 请求;为此,UserModel 中存储的 api 密钥是必需的。可以只订阅用户属性,但我读到了避免在服务内部进行订阅。

问题

如何将此订阅与管道/映射连接?我的方法是以下代码;但这感觉像是糟糕的代码。

suggest(term: string): Observable<ProjectModel[]> {
      return this.userSrv.user.pipe(
         mergeMap((user: UserModel) => {
            const options = {params: {'access-token': user.accessToken}};

            return this.http.get<ProjectModel[]>(this.conf.url, options).pipe(
               map(response => {
                  // mapping the projects ...
                  return projects;
               })
            );
         })
      );
   }

【问题讨论】:

  • 您是否将此访问令牌添加到客户端应用程序的每个传出请求中?
  • 可以,登录过程除外。

标签: angular ionic-framework rxjs angular-httpclient


【解决方案1】:

如上一个答案中所述,HTTP Interceptor 最适合您的用例。

基本思路如下所示:

import {Injectable} from '@angular/core';
import {HttpEvent, HttpInterceptor, HttpHandler, HttpRequest} from '@angular/common/http';
import {mergeMap,map} from 'rxjs/operators';
import {Observable} from 'rxjs';

function isLoginRequest(req: HttpRequest<any>): boolean {
    // implement
}

@Injectable()
export class AccessTokenInterceptor implements HttpInterceptor {
  constructor(private readonly userService: UserService){}

  intercept(req: HttpRequest<any>, next: HttpHandler):
    Observable<HttpEvent<any>> {
    if(isLoginRequest(req)) return next.handle(req);
    return this.userService.user.pipe(
      map(user => req.clone({setParams:{'access-token': user.accessToken}})),
      mergeMap(req => next.handle(req))
    );
  }
}

不要忘记按照文档中的示例注册此拦截器。

通过使用这个,你原来的服务功能被简化为:

suggest(term: string): Observable<ProjectModel[]> {
  return this.http.get<ProjectModel[]>(this.conf.url).pipe(
           map(response => {
              // mapping the projects ...
           })
        );
}

【讨论】:

    【解决方案2】:

    您应该使用 HttpInterceptor :https://angular.io/api/common/http/HttpInterceptor,而不是向每个请求传递访问令牌。 它会在您执行的每个 http 请求中添加 AccessToken。 将用户存储在 UserService 的 BehaviorSubject 中,并在 Interceptor 中使用。

    在此之后,您的函数将如下所示:

    suggest(term: string): Observable<ProjectModel[]> {
        return this.http.get<ProjectModel[]>(this.conf.url + '/my-endpoint');
    }
    

    【讨论】:

    • 谢谢。我已经在使用 BehaviorSubject(请参阅编辑)-但是在 HttpInterceptor 中访问 BehaviorSubject 的最佳做法是什么?假设另一种情况是“有必要”进行嵌套订阅;最佳实践?
    • 拦截函数要求一个 Observable,这样你就可以在管道中使用 BehaviorSubject 来获取访问令牌(或其他东西)而无需订阅。
    猜你喜欢
    • 2019-10-01
    • 2019-12-20
    • 2022-01-07
    • 2016-06-20
    • 1970-01-01
    • 1970-01-01
    • 2020-01-13
    • 2020-04-19
    • 2020-11-19
    相关资源
    最近更新 更多