![](/img/trans.png)
[英]Angular HTTP Client Interceptor not working when returning custom Observable
[英]Angular HTTP Client returning null inside interceptor
在下面的代碼中,我發布了兩個代碼在控制台中返回的示例。 對於攔截器,我應該返回一個可觀察的。 我已將本地存儲承諾轉換為使用switchmap的可觀察對象。 我仍然使用這種方法返回null。 我的observable包含在函數中,所以我應該得到一個null以外的值。 謝謝!
Interceptor.js
import { fromPromise } from 'rxjs/observable/fromPromise';
accessToken: string;
emptyToken: any;
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
//example
this.storage.get('token').then((val) => {
this.accessToken = val
return console.log(this.accessToken, ' this is returning null value for token')
})
//trying to return actual header
return fromPromise(this.storage.get('token')).switchMap(access => {
this.accessToken = access
console.log(this.accessToken, ' this is returning null value for token')
const authReq = req.clone({
setHeaders: {
Authorization: this.accessToken
}
});
return next.handle(authReq)
})
}
}
我已經添加了下面更新的代碼,請忽略上面的代碼並仍然得到相同的結果。 作為下面所述的答案之一,它們在所有假設中都是正確的。 歸結為將令牌置於登錄observable內部。 問題不在於攔截器,代碼工作正常。 我需要以某種方式獲取我的異步值而不返回null。 this.storage.ready()方法也給了我相同的結果。 我的攔截器后調用了auth服務,因此我還沒有生成任何令牌。 我將如何首先調用我的身份驗證服務?
login(userName: string, password: string, route: string = null): any {
this.storage.set('tokens', 'able to retrieve this token value inside interceptor');
this.logout(false);
this.doLogin(userName, password)
.subscribe(response => {
let token:string = JSON.stringify(response.access_token);
this.storage.set('token', token)
}
}
interceptor.ts
get(url: string, options?: RequestOptionsArgs): Observable<Response> {
return Observable.fromPromise(
this.getRequestOptionArgs(options)
).mergeMap((options) => {
return super.get(url, options)
})
}
private getRequestOptionArgs(options?: RequestOptionsArgs) {
return this.storage.get('token').then((token) => {
console.log(token, 'token for the get')
if (options == null) {
options = new RequestOptions();
}
if (options.headers == null) {
options.headers = new Headers();
}
if (token !== null) {
options.headers.append('Authorization', 'Bearer ' + token);
}
options.headers.append('Content-Type', 'application/json');
return options;
});
}
我認為問題是你的StorageService
返回null
,而不是Interceptor的問題。
存儲服務使用Promise,這意味着它只會發出一個值。 如果你得到null
那么我猜你還沒有存儲中的令牌。
你從哪里獲得令牌? 服務器? 在這種情況下,第一個請求將不具有令牌。
但是......你已經說過,當你直接(同步)訪問localStorage
時,你得到一個值,一切正常。 所以我假設你已經有一個存儲令牌。
看看Ionic Storage的文檔,看起來還有另一種可能性。 存儲有一個ready
方法,所以也許商店還沒准備好。
您可以嘗試這樣的事情(未經測試),在嘗試獲取令牌之前,首先等待准備好的承諾解決:
import { Injectable } from '@angular/core';
import { HttpErrorResponse, HttpHandler, HttpInterceptor, HttpRequest } from '@angular/common/http';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/mergeMap';
import 'rxjs/add/observable/fromPromise';
@Injectable()
export class Interceptor implements HttpInterceptor {
constructor (private storage: StorageService) {}
intercept (request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
// chain the ready promise with the get token promise
return Observable.fromPromise(this.storage.ready().then(this.storage.get('token')))
.mergeMap((token: string) => {
const authReq = request.clone({
setHeaders: {
Authorization: token
}
});
return next.handle(authReq);
});
}
}
只是一個猜測......
問題正在發生,因為this.storage.get('token').then()
是一個promise,而promise .then()
只是在已經進行調用之后設置this.token
。
您需要向構造函數添加一個方法,該方法將從localStorage獲取令牌並將其存儲在攔截器上。 這將在您創建攔截器實例時觸發。
攔截器
import { Injectable } from '@angular/core';
import { HttpEvent, HttpInterceptor, HttpHandler, HttpRequest } from '@angular/common/http';
import { Observable } from 'rxjs/Observable';
const tokenStorageKey = 'token'
const tokenType = 'Bearer'
@Injectable()
export class AuthInterceptor implements HttpInterceptor {
private token: any;
constructor (private storage: StorageService) {
this.getToken();
}
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
let contentTypeSet = req.headers.set('Content-Type', 'application/json');
let authHeaderSet = contentTypeSet.set('Authorization', `${tokenType} ${this.token}`)
const authReq = req.clone({
headers: authHeaderSet
});
return next.handle(authReq);
}
}
private getToken(): void{
this.storage.get(tokenStorageKey).then((token: any) => {
this.token = token;
});
}
}
您還可以添加一個publish
事件,使所有已訂閱的方法都知道已獲取令牌。
幸運的是,在開發JWT解決方案時(甚至在本機攔截器存在之前),我已經不得不解決這種情況。
你真正想要的行為是這樣的:
發送請求
1.1。 如果本地存儲中不存在令牌,則附加“授權承載”
1.2。 否則從本地存儲中讀取令牌
獲得成功“授權”令牌的回復
還請記住,每個請求都會調用所有攔截器 。 此外,在注冊時,訂單很重要! 流程是這樣的:
請求創建 - > interceptorA,interceptorB - >發送到服務器 - >從服務器接收 - > interceptorA,interceptorB - > subscribe函數 所以這就是你的
this.doLogin(...).subscribe(...)
發生的事情this.doLogin(...).subscribe(...)
所以,我所做的並建議你,有兩種類型的攔截器。 如果當前用戶不存在令牌,則將附加授權承載的一個AuthorizationAppendingInterceptor
。
第二個應該是...... 比如AuthorizationReceivedInterceptor
,它會將事件過濾為HttpRespone
一個實例,並查找標題Authorization ,它將在成功登錄時由服務器發送。 只有這樣,在接收攔截器中,您才能將令牌寫入本地存儲。
一些代碼:
AuthorizationAppendingInterceptor
鑒於this.authService
是處理令牌內容的實例
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
// Get the auth header from the service.
const headers: HttpHeaders = this.authService.appendAuthorizationHeader(req.headers);
// Clone the request to add the new header.
const authReq: HttpRequest<any> = req.clone({ headers: headers });
// Pass on the cloned request instead of the original request.
return next.handle(authReq);
}
AuthorizationReceivingInterceptor
鑒於AuthService.AUTHORIZATION_HEADER = 'Authorization'
和this.authService
是處理令牌內容的實例。
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
return next
.handle(req)
.do(event => {
if (event instanceof HttpResponse) {
const response: HttpResponse<any> = event as HttpResponse<any>;
if (!response.headers || !response.headers.has(AuthService.AUTHORIZATION_HEADER)) {
return;
}
this.authService.setToken(response.headers.get(AuthService.AUTHORIZATION_HEADER));
}
}
);
}
只是為了完整起見,在這里不用的appendAuthorizationHeader
方法從authService
public appendAuthorizationHeader(headers: HttpHeaders): HttpHeaders {
if (!this.token || !this.token.canAppendToHeaders()) { return; }
return headers.append(AuthService.AUTHORIZATION_HEADER, this.token.getTokenString());
}
實現此代碼后,您的身份驗證機制應該無需在應用程序邏輯中進行任何調整,並且還支持自動重新登錄,因為一旦令牌無效, AuthorizationAppendingInterceptor
將自動觸發,之后, AuthorizationReceivingInterceptor
將處理您的更新。本地存儲令牌。
為什么你不為你的服務做這樣的事情:
createAThing(userId :string, timestamp :string, reason: string): Promise<Appointment> {
let bodySearchParam = new URLSearchParams();
bodySearchParam.append('userId', userId );
bodySearchParam.append('timestamp', this.datetotimestamp(timestamp).toString());
bodySearchParam.append('reason', reason);
let body = bodySearchParam.toString();
console.log(body) // check if my body is ok
return this.http.post(this.AppointmentUrlWrite,body)
.toPromise()
.then(response =>
console.log(response.json().data)
)
.catch(this.handleError);
}
以我的拙見,它會比你的方法更好
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.