简体   繁体   中英

Using a promise to return a request object from an HTTP interceptor

I have been scratching my head at this for hours and I am hoping someone can help me and guide me. So, I am developing an Angular 7 application authentication module. One of the requirements is to develop an HTTP interceptor to add an Authorization (JWT) token and also to handle all error messages.

I am using an NPM package to handle localstorage of the tokens. This package uses set and get methods to store and returns a promise rather than the actual value of the token.

Now, my problem is in my interceptor function as we can see below. I have tried to comment where I am getting stuck.

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

    // Trying to get the token here but this returns a promise
    // this.token is a service for managing storage and retrieving of tokens
    const token = this.token.getToken();

    // If token is got, set it in the header
    // But when i console log, i see [object promise] other than the token
    if (token) {
        request = request.clone({
            headers: request.headers.set('Authorization', 'Bearer ' + token)
        });
    }

    return next.handle(request).pipe(catchError(err => {
        // Logs out the user if 401 error
        if (err.status === 401) {
            this.token.remove()
                .then(() => {
                    this.auth.changeAuthStatus(false);
                    this.router.navigateByUrl('/login');
                });
        }

        // Returns the error message for the user to see
        // for example in an alert
        const error = err.error.message || err.statusText;
        return throwError(error);
    }));
}

I hope I have well explained this problem. I have tried using async before the interceptor function but I get a red nasty error saying TS1055: Type 'typeof Observable' is not a valid async function return type in ES5/ES3 because it does not refer to a Promise-compatible constructor value. Types of parameters 'subscribe' and 'executor' are incompatible. TS1055: Type 'typeof Observable' is not a valid async function return type in ES5/ES3 because it does not refer to a Promise-compatible constructor value. Types of parameters 'subscribe' and 'executor' are incompatible. .

I will appreciate any help towards fixing this issue.

Thank you!

to incorporate asynchronous processing into your interceptor, you want to promote your promise to an observable, and switchMap your observables together, returning the proper request:

import { from as observableFrom } from "rxjs";
import { switchMap } from "rxjs/operators";

intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    return observableFrom(this.token.getToken()).pipe(
        switchMap(token => {

            // do something with your promise-returned token here

            return next.handle(request).pipe(catchError(err => {
                // Logs out the user if 401 error
                if (err.status === 401) {
                    this.token.remove()
                        .then(() => {
                            this.auth.changeAuthStatus(false);
                            this.router.navigateByUrl('/login');
                        });
                }

                // Returns the error message for the user to see
                // for example in an alert
                const error = err.error.message || err.statusText;
                return throwError(error);
            }));
        })
    );
}

have not tested this code directly, so i apologize for any typos, but it should get you where you want to go.

1) promote your promise to an observable with from

2) chain your observables with switchMap

i notice you're not actually using the token returned in your example, you would do that in the function within switchMap that receives the result of the promise

Try to take a token directly from local storage. For that when you get token store that token to local storage using token service method.

Try below code:

token.service.ts

setToken(token) {
    localStorage.setItem('app-token', JSON.stringify(token));
}

getToken() {
    return JSON.parse(localStorage.getItem('app-token'));
}

Interceptor code

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

//This token is retrieved from local storage
const token = this.token.getToken();

// If token is got, set it in the header
// But when i console log, i see [object promise] other than the token
if (token) {
    request = request.clone({
        headers: request.headers.set('Authorization', 'Bearer ' + token)
    });
}

return next.handle(request).pipe(catchError(err => {
    // Logs out the user if 401 error
    if (err.status === 401) {
        this.token.remove()
            .then(() => {
                this.auth.changeAuthStatus(false);
                this.router.navigateByUrl('/login');
            });
    }

    // Returns the error message for the user to see
    // for example in an alert
    const error = err.error.message || err.statusText;
    return throwError(error);
}));
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM