簡體   English   中英

Angular HTTP Client在攔截器內返回null

[英]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。 如果本地存儲中不存在令牌,則附加“授權承載”

    1.2。 否則從本地存儲中讀取令牌

  2. 獲得成功“授權”令牌的回復

  3. 將此令牌保存到本地存儲

還請記住,每個請求都會調用所有攔截器 此外,在注冊時,訂單很重要! 流程是這樣的:

請求創建 - > 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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM