TypeScript - איך אפשר לגשת למאפיין מתוך מתודת שירות ב-Angular?


אני חדש ב-Angular ואני מנסה להעביר טוקן מ-Spotify API לפונקציית שירות ב-Angular, אז יש לי נקודת קצה שמקבלת את הטוקן ועובדת, אבל כשאני קורא לפונקציה getQuery() אובייקט הפלט של 'headers' נראה משהו כמו:

{
    Authorization: Bearer undefined
}

אז בגלל זה, כשאני רוצה לבצע בקשה, זה מחזיר סטטוס 401 כי אסימון הגישה שלי לא נכון.

ככה נראה השירות שלי ושיטת הבנאי:

import { Injectable } from '@angular/core';
import { HttpClient,HttpHeaders } from '@angular/common/http';
import { map } from 'rxjs/operators';



@Injectable({
  providedIn: 'root'
})
export class SpotifyService {


  private token:string

  constructor(
    private http:HttpClient,

  ) {
    const clientId = 'my client id';
    const clientSecret = 'my client secret';
    this.getToken(clientId, clientSecret);

  }

וזה מה שנראה הפונקציה getToken() שלי:

getToken(clientId:string, clientSecret:string){
    const urlRequest = `https://myendpoint/${clientId}/${clientSecret}`;
    this.http.get(urlRequest)
              .subscribe( (data:any) =>{
                this.token = data.access_token;
              });
  }

בנקודה זו הכל בסדר, אבל כשאני קורא לשירות זה מתוך קומפוננטה ואני קורא לשיטת שירות אחרת, נראה שהקונסטרקטור לא מתבצע כי אני מקבל את בעיית ה-undefined בשיטת השירות הזו:

getQuery( query:string ){

    const url = `https://spotifyapi/${query}`;

    const headers = new HttpHeaders({
      'Authorization': `Bearer ${this.token}`
    });
    console.log(headers);
    return this.http.get(url,{headers});
  }

השתמשתי ב-console.log() כדי לבדוק אם אני מקבל את הטוקן ב-getToken() וזה עובד, אבל נראה שאני לא יכול לגשת אליו מהשיטה getQuery().

אני פשוט רוצה לעשות את הטוקן נגיש בשיטה הזאת כדי שאוכל לבצע בקשות.

תשובה #1

מכיוון ש-this.http.get היא קריאה אסינכרונית, עליך להמתין עד שהקריאה תסתיים. ניתן לעשות זאת כך בהתאם לאופן שבו אתה מממש זאת:

@Injectable({ providedIn: 'root' })
export class SpotifyService {

   // Changing its type from a simple string to an Observable<string>
   private token$: Observable<string>;

   constructor(private http:HttpClient) {
     const clientId     = 'my client id';
     const clientSecret = 'my client secret';

     // Initialize the token
     this.token = this.getToken(clientId, clientSecret);
   }

   getToken(clientId:string, clientSecret:string): Observable<any> {
      const urlRequest = `https://myendpoint/${clientId}/${clientSecret}`;

      // Call the token api and get only its "access_token"
      return this.http.get(urlRequest).pipe(map(data => data.access_token));
   }

   getQuery( query:string ){

     const url = `https://spotifyapi/${query}`;

     // Subscribe to the token$ observable and assign the token on your headers
     this.token$.subscribe(token => {

       const headers = new HttpHeaders({ 'Authorization': `Bearer ${this.token}` });

       console.log(headers);

       return this.http.get(url, {headers});
     });

   }


}

דרך נוספת היא להשתמש ב-HTTP Interceptor

1.) לאחר שתתחבר, בדרך כלל זה ייתן לך את הטוקן. שמור את הטוקן הזה ב-localStorage שלך

localStorage.setItem('token:id', token);

2.) צור auth.interceptor.ts

עם זה, בכל פעם שתתקשר ל-API שלך, זה יכניס אוטומטית את הכותרות שלך

@Injectable({ providedIn: 'root' })
export class AuthInterceptor implements HttpInterceptor {

   intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {

      const token = localStorage.getItem('token:id');

      if (token) {
         const cloned = req.clone({ headers: req.headers.set('Authorization', `Bearer ${token}`) });
         return next.handle(cloned);
      }
      else return next.handle(req);
  }
}

3.) יבא את זה ל-AppModule או ל-CoreModule (אם יש לך)

@NgModule({
  imports: [],
  providers: [
    {
       provide: HTTP_INTERCEPTORS,
       useClass: AuthInterceptor,
       multi: true
    }
  ]
})
export class AppModule {}

תשובה #2

נו, אני רואה כמה בעיות בדרך שבה אתה מנסה לפתור את הבעיה. כפי שהוצע בהערות, ייתכן שהאסימון לא נשלף לפני שבוצעה הקריאה. שקול לבצע את השינויים הבאים

  1. השתמש ב-APP_INITIALIZER כדי לטעון את הטוקן מראש, כך מובטח שיהיה לך את הטוקן לפני שהאפליקציה שלך מתחילה לטעון - תסתכל על - https://www.tektutorialshub.com/angular/angular-how-to-use-app-initializer/
  2. אם אסימון האימות נדרש בכל קריאה, לא כדאי להוסיף את הכותרת בכל שירות, שקול להוסיף חוצץ HTTP כדי להוסיף את כותרת האימות לפני כל שירות - https://medium.com/@ryanchenkie_40935/angular-authentication-using-the-http-client-and-http-interceptors-2f9d1540eb8