28

Can I use promise within HttpInterceptor? For example:

export class AuthInterceptor implements HttpInterceptor{
this.someService.someFunction()
    .then((data)=>{
       //do something with data and then
       return next.handle(req);
    });
}

why I need this? because I need to get a token to add to request header before making the request to the server.

My interceptor:

@Injectable()
export class AuthInterceptor implements HttpInterceptor{

    constructor(private authService: AuthService){}

    intercept(req: HttpRequest<any>, next: HttpHandler) : Observable<HttpEvent<any>>{
        console.log('Intercepted!');
        // return next.handle(req);
        this.authService.getToken()
            .then((token)=>{
                console.log(token);
                const reqClone = req.clone({
                    headers: req.headers
                            .set('Authorization', 'Bearer ' + token)
                            .append('Content-Type', 'application/json')
                });
                console.log(reqClone);
                return next.handle(reqClone);
            })
            .catch((err)=>{
                console.log('error in interceptor' + err);
                return null;
            });
    }
}

Request:

this.http.post(this.baseURL + 'hero', data)
                    .subscribe(
                            (res: any) => {
                                console.log('Saved Successfully.');
                                console.log(res);
                            },
                            (err: any) => {
                                console.log('Save Error.' + err);
                            }
                        );

Problems I am facing:

->I get this error before the promise is resolved.

You provided 'undefined' where a stream was expected. You can provide an Observable, Promise, Array, or Iterable.

Promise resloves and I get my token but after the error.

mario.van.zadel
  • 2,919
  • 14
  • 23
nightElf91
  • 650
  • 1
  • 7
  • 28
  • I just checked my xhr requests under the developer tools, there's no 'hero' request there.So, the interceptor is not continuing the request? – nightElf91 Aug 31 '17 at 12:11

3 Answers3

61

UPDATE: using rxjs@6.x

import { from, Observable } from 'rxjs';
import { switchMap } from 'rxjs/operators';

@Injectable()
export class AuthInterceptor implements HttpInterceptor {

    constructor(private authService: AuthService){}

    intercept(request: HttpRequest<any>, next: HttpHandler) : Observable<HttpEvent<any>>{
        return from(this.authService.getToken())
              .pipe(
                switchMap(token => {
                   const headers = request.headers
                            .set('Authorization', 'Bearer ' + token)
                            .append('Content-Type', 'application/json');
                   const requestClone = request.clone({
                     headers 
                    });
                  return next.handle(requestClone);
                })
               );
    }
}

ORIGINAL ANSWER

Yes, you could inject the required service into the constructor method of the interceptor, and in the implementation of intercept retrieve the value, create a new updated http request and handle it.

I'm not good with promises, so you could try the following:

import { fromPromise } from 'rxjs/observable/fromPromise';

@Injectable()
export class AuthInterceptor implements HttpInterceptor{

    constructor(private authService: AuthService){}

    intercept(req: HttpRequest<any>, next: HttpHandler) : Observable<HttpEvent<any>>{
        return fromPromise(this.authService.getToken())
              .switchMap(token => {
                   const headers = req.headers
                            .set('Authorization', 'Bearer ' + token)
                            .append('Content-Type', 'application/json');
                   const reqClone = req.clone({
                     headers 
                    });
                  return next.handle(reqClone);
             });
    }
}
mario.van.zadel
  • 2,919
  • 14
  • 23
Jota.Toledo
  • 27,293
  • 11
  • 59
  • 73
  • thanks bro. I have updated the question. hope its clearer now. – nightElf91 Aug 31 '17 at 12:06
  • It's working. Thanks a lot. was it not working because getToken() returns a promise not observable? @Jota.Toledo – nightElf91 Aug 31 '17 at 16:20
  • The return type of intercept has to be an observable, I dont now how to work with promises, thats why I transformed the promise into observable and applied operations in it. I guess your implementation wasnt returning an observable. If my answer helped you, please select it as answer so that ur question gets marked as solved :) – Jota.Toledo Aug 31 '17 at 16:24
  • Yeah, I changed my function to observable and it's now working without fromPromise. – nightElf91 Aug 31 '17 at 17:12
  • Perfect, stick to observables; they can do all what a promise do and more :) – Jota.Toledo Aug 31 '17 at 17:13
  • @Jota.Toledo May I ask why are you using `switchMap` here? From my understanding, `getToken` is converted to an observable, but what is so special about getting an auth token that can cause it to send more than one observable value? – Jeffrey Wen May 22 '20 at 08:50
  • @JeffreyWen you could replace it with a mergeMap. There is no need to _exclusively_ use switchMap for the reason that you pointed out :) – Jota.Toledo May 22 '20 at 09:50
  • @Jota.Toledo I'm still relatively new to reactive programming, but why do we need to use things like switchMap, mergeMap, concatMap, etc. for something as simple as getting an Auth Token (probably from local storage?). Is omitting the switchMap/mergeMap entirely a viable solution? – Jeffrey Wen May 22 '20 at 16:58
  • 1
    @JeffreyWen they are required in order to map the token to an inner-observable (the result of next.handle). The idea is to basically map the "token stream" (with just 1 event) into a new one. Because of the method´s signature, there are no viable approaches without using any of the mentioned operators. – Jota.Toledo May 23 '20 at 20:39
  • @Jota.Toledo Thank you for clariying that! – Jeffrey Wen May 24 '20 at 00:02
  • Something to note: you will not see the headers added to your clone request in your interceptor due to lazy setting of the headers. They won't be materialized until the final handler runs. I chased my tail on this wondering why I couldn't see my auth header. I traced into the angular source and figured it out. – Don Oct 20 '20 at 20:28
9

for RxJS 6+ I've updated Jota.Toledo's Answer:

import { fromPromise } from 'rxjs/observable/fromPromise';

@Injectable()
export class AuthInterceptor implements HttpInterceptor{
constructor(private authService: AuthService){}
intercept(req: HttpRequest<any>, next: HttpHandler) : Observable<HttpEvent<any>>{
    return fromPromise(this.authService.getToken())
          .pipe(switchMap(token => {
               const headers = req.headers
                        .set('Authorization', 'Bearer ' + token)
                        .append('Content-Type', 'application/json');
               const reqClone = req.clone({
                 headers 
                });
              return next.handle(reqClone);
         }));
 }  
}
mario.van.zadel
  • 2,919
  • 14
  • 23
Capripio
  • 474
  • 11
  • 21
4

My turn (Thanks to Jota.Toledo and Capripio) :

1) Switch "fromPromise" to "from" --> fromPromise does not exist on type Observable

2) Fix 'application/json' quote

import { Observable, from } from 'rxjs';
import { switchMap } from 'rxjs/operators';

@Injectable()
export class AuthInterceptor implements HttpInterceptor {

  constructor(private authService: AuthService){}

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

      return from(this.authService.getToken()).pipe(
        switchMap(token => {
            const headers = req.headers
                .set('Authorization', 'Bearer ' + token)
                .append('Content-Type', 'application/json');
            const reqClone = req.clone({
                headers
            });
            return next.handle(reqClone);
        }));
   }
}

My version of RxJs: "6.2.2" Hope that helped !

Echo
  • 91
  • 5