Angular - 如何将访问令牌从可观察对象添加到传出 HTTP 请求?
Angular - How to add an access token from an observable to an outgoing HTTP request?
在我的 UserService
中,我有一个用户 Observable
对象,它包含登录用户的 UserModel
。为了测试,我在 ngOnInit()
登录过程:
this.userService.authenticate('###', '###')
.subscribe(res => console.log('authenticated'));
private userSource = new BehaviorSubject<UserModel>(null);
public user = this.userSource.asObservable();
我的 UserModel 提供了一个名为 authKey 的属性,用于 API 身份验证。
在我的 ProjectService
中,我想做一个 api 请求;为此,UserModel
中存储的 api 密钥是必需的。可能只订阅用户属性,但我读到关于避免在服务内部进行订阅。
问题
如何将此订阅与 pipes/mapping 相关联?我的方法是以下代码;但这感觉像是糟糕的代码。
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;
})
);
})
);
}
您应该使用 HttpInterceptor,而不是向每个请求传递访问令牌:https://angular.io/api/common/http/HttpInterceptor。
它会在您执行的每个 http 请求中添加 AccessToken。
将用户存储在您的 UserService 的 BehaviorSubject 中,并在拦截器中使用它。
在此之后,您的函数将如下所示:
suggest(term: string): Observable<ProjectModel[]> {
return this.http.get<ProjectModel[]>(this.conf.url + '/my-endpoint');
}
如前一个答案所述,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 ...
})
);
}
在我的 UserService
中,我有一个用户 Observable
对象,它包含登录用户的 UserModel
。为了测试,我在 ngOnInit()
登录过程:
this.userService.authenticate('###', '###')
.subscribe(res => console.log('authenticated'));
private userSource = new BehaviorSubject<UserModel>(null);
public user = this.userSource.asObservable();
我的 UserModel 提供了一个名为 authKey 的属性,用于 API 身份验证。
在我的 ProjectService
中,我想做一个 api 请求;为此,UserModel
中存储的 api 密钥是必需的。可能只订阅用户属性,但我读到关于避免在服务内部进行订阅。
问题
如何将此订阅与 pipes/mapping 相关联?我的方法是以下代码;但这感觉像是糟糕的代码。
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;
})
);
})
);
}
您应该使用 HttpInterceptor,而不是向每个请求传递访问令牌:https://angular.io/api/common/http/HttpInterceptor。 它会在您执行的每个 http 请求中添加 AccessToken。 将用户存储在您的 UserService 的 BehaviorSubject 中,并在拦截器中使用它。
在此之后,您的函数将如下所示:
suggest(term: string): Observable<ProjectModel[]> {
return this.http.get<ProjectModel[]>(this.conf.url + '/my-endpoint');
}
如前一个答案所述,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 ...
})
);
}