Angular HTTP Interceptor - 在多模块应用程序中显示微调器

时间:2018-01-19 08:22:26

标签: angular angular5 angular-http-interceptors

我正在尝试显示ng4-loading-spinner微调器,以便对我的API进行HTTP调用。

我的代码基于以下链接中的示例:

我的Angular 5应用程序有多个多个模块。 HTTP拦截器位于“服务”模块中。

我认为我遇到了依赖注入问题,因为当我使用Chrome Dev Tools调试代码时,代码HTTP拦截器代码无法执行。

API-interceptor.ts

import 'rxjs/add/operator/do';
import 'rxjs/add/operator/catch'
import { Observable } from 'rxjs/Observable';
import { Injectable } from '@angular/core';
import {
    HttpEvent,
    HttpInterceptor,
    HttpHandler,
    HttpRequest,
    HttpResponse
} from '@angular/common/http';
import { Ng4LoadingSpinnerService } from 'ng4-loading-spinner';

@Injectable()
export class ApiInterceptor implements HttpInterceptor {

    private count: number = 0;

    constructor(private spinner: Ng4LoadingSpinnerService) { }

    intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
        this.count++;

        if (this.count == 1) this.spinner.show();

        let handleObs: Observable<HttpEvent<any>> = next.handle(req);

        handleObs
            .catch((err: any) => {
                this.count--;
                return Observable.throw(err);
            })
            .do(event => {
                if (event instanceof HttpResponse) {
                    this.count--;
                    if (this.count == 0) this.spinner.hide();
                }
            });

        return handleObs;
    }

}

api.service.ts

import { Injectable, Inject } from '@angular/core';
import { Http, Response, Headers, RequestOptions } from '@angular/http';
import { Observable } from 'rxjs/Observable';

import { TokenService } from './token.service';

@Injectable()
export class ApiService {

    constructor(
        private http: Http,
        private session: TokenService,
        @Inject('BASE_URL') private baseUrl) { }

    get(entityRoute: string): Observable<Response> {
        let apiRoute = this.getApiRoute(entityRoute);
        let options = this.generateRequestOptions();

        return this.http.get(apiRoute, options);
    }

    post<T>(entityRoute: string, entity: T): Observable<Response> {
        let apiRoute = this.getApiRoute(entityRoute);
        let options = this.generateRequestOptions();

        return this.http.post(apiRoute, entity, options);
    }

    put<T>(entityRoute: string, entity: T): Observable<Response> {
        let apiRoute = this.getApiRoute(entityRoute);
        let options = this.generateRequestOptions();

        return this.http.post(apiRoute, entity, options);
    }

    private getApiRoute(entityRoute: string): string {
        return `${this.baseUrl}api/${entityRoute}`;
    }

    private generateRequestOptions(): RequestOptions {
        let headersObj = null;
        let accessToken = this.session.getAccessToken();

        if (accessToken) {
            headersObj = {
                'Content-Type': 'application/json',
                'Authorization': 'Bearer ' + accessToken
            };
        } else {
            headersObj = {
                'Content-Type': 'application/json'
            };
        }

        let headers = new Headers(headersObj);
        return new RequestOptions({ headers: headers });
    }

}

services.module.ts

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { HttpModule } from '@angular/http';
import { Ng4LoadingSpinnerModule } from 'ng4-loading-spinner';

import {
    ApiInterceptor,
    ApiService,
    TokenService
} from './index';

@NgModule({
    imports: [
        CommonModule,
        HttpModule,
        Ng4LoadingSpinnerModule
    ],
    providers: [
        ApiInterceptor,
        ApiService,
        TokenService
    ]
})
export class ServicesModule { }

export * from './index';

app.module.ts

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { HTTP_INTERCEPTORS } from '@angular/common/http';
import { Ng4LoadingSpinnerModule } from 'ng4-loading-spinner';

import { BootstrapModule } from './bootstrap/bootstrap.module';
import { ServicesModule, ApiInterceptor } from './services/services.module';
import { AppComponent } from './app-component';

@NgModule({
    bootstrap: [ AppComponent ],
    imports: [
        BrowserModule,
        Ng4LoadingSpinnerModule.forRoot(),
        BootstrapModule,
        ServicesModule
    ],
    providers: [
        {
            provide: 'BASE_URL',
            useFactory: getBaseUrl
        },
        {
            provide: HTTP_INTERCEPTORS,
            useClass: ApiInterceptor,
            multi: true,
        }
    ]
})
export class AppModule {
}

export function getBaseUrl(): string {
    return document.getElementsByTagName('base')[0].href;
}

4 个答案:

答案 0 :(得分:3)

问题是ApiService使用Http中的@angular/http代替HttpClient中的@angular/common/http

所以ApiInterceptor无法拦截。

答案 1 :(得分:2)

忘记reportProgress:是的。问题是我们必须区分“做”的事件。而且,我们必须对调用进行计数,因此拦截器必须像

contador: number = 0;

intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
        this.contador++;
        if (this.contador === 1) {
            this.spinner.show();
        }
        let handleObs: Observable<HttpEvent<any>> = next.handle(req);
        handleObs
        .catch((err: any) => { //If an error happens, this.contador-- too
            this.contador--;
            return Observable.throw(err);
        })
        .do(event => {
           if (event instanceof HttpResponse) { //<--only when event is a HttpRespose
              this.contador--;
              if (this.contador==0)
                 this.spinner.hide();
           }
        });

        return handleObs;
    }

答案 2 :(得分:0)

对于处理此问题的每个人,OP的代码现在都可以正常工作,除了加载程序似乎没有隐藏的其余问题。解决方法是,在 .catch .do 链之后,订阅到Observable,就像这样:

handleObs
    .catch((err: any) => {
        this.count--;
        return Observable.throw(err);
    })
    .do(event => {
        if (event instanceof HttpResponse) {
            this.count--;
            if (this.count == 0) this.spinner.hide();
        }
    })
    .subscribe(); /* <---------- ADD THIS */

return handleObs;

此后,代码应该可以正常工作,并且当计数器达到0时,加载程序将隐藏。还要感谢上述所有答案!

答案 3 :(得分:0)

对于那些计数器有问题的人,即使没有待处理的请求也永远不会再达到零: 增加计数器时,我不得不额外检查事件的类型:

 if (event instanceof HttpResponse) {
    this.counter.dec();
 } else {
    this.counter.inc();
 }

否则,我遇到了HttpResponse的情况,这也增加了我的计数器。 通过以上检查,我所有组件的计数器都恢复为零。

另外,请确保返回的http错误(例如401)也会使您的计数器减少,否则计数器将永远不会再为零。 为此:

return next.handle(req).pipe(tap(
  (event: HttpEvent<any>) => {
    if (event instanceof HttpResponse) {
        this.counter.dec();
    }
  },
  err => {
    if (err instanceof HttpErrorResponse) {
      this.counter.dec();
    }
  }
));