【问题标题】:Angular HTTP Interceptor - Display spinner in multi-module appAngular HTTP Interceptor - 在多模块应用程序中显示微调器
【发布时间】:2018-06-28 10:34:48
【问题描述】:

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

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

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

我认为我遇到了依赖注入问题,因为当我使用 Chrome 开发工具调试我的代码时,代码 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;
}

【问题讨论】:

  • 我认为您必须使用 { reportProgress: true,} 发布并订阅事件。检查angular.io/guide/http#listening-to-progress-events
  • @Eliseo,我在问题中添加了我的ApiService 代码。我不确定在哪里设置reportProgress,因为RequestOptions 没有reportProgress 属性。当然,我不必更改该服务中的所有代码来使用HttpRequest 而不是Http

标签: angular angular5 angular-http-interceptors


【解决方案1】:

问题是ApiService 使用来自@angular/httpHttp 而不是来自@angular/common/httpHttpClient

所以ApiInterceptor 没有什么可以拦截的。

【讨论】:

    【解决方案2】:

    忘记报告进度:真。问题是我们必须区分“做”的事件。而且,我们必须对调用进行计数,所以拦截器必须像

    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;
        }
    

    【讨论】:

    • 我添加了这段代码,但它并没有解决我的依赖注入问题。 ApiInterceptor 永远不会被点击(当我调试时)并且微调器永远不会显示。我已更新我的问题以包含您的代码。
    • 对不起,我以前没有注意到我觉得很愚蠢:拦截器只能使用 HttpClient(不是 http)。更改您的 ApiService 构造函数
    • 切换HttpClient 工作...谢谢。但是,微调器永远不会被隐藏。 do 是隐藏微调器的合适位置吗?
    • 我不确定,也许使用 finally... 我只是“调整”angular.io/guide/http#logging 的示例
    • 谢谢,成功了。您是否要发布另一个答案,说明问题是我需要在我的ApiService 中使用HttpClient 而不是Http,我会将其标记为解决方案?
    【解决方案3】:

    对于跟进此问题的每个人,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 时加载器将隐藏。也感谢以上所有答案的贡献!

    【讨论】:

    • 当我添加订阅链时,我在浏览器的网络选项卡中看到它正在执行所有请求两次。有人知道为什么会这样吗?
    • @nickgowdy 能否请您提供网络选项卡的屏幕截图?请注意,上面的整个代码,包括 .subscribe() 方法,只“监听”主要的 Http 调用,就这段代码而言,它不会改变它们。我们只能假设您指的是 XHR 请求生成两个请求的事实,一个是 OPTION(不是实际请求,只是实际请求之前的标头),另一个是实际请求(GET/POST 等)实际数据)。无论如何,我认为我们需要更多信息。最好的问候!
    • 你要我创建一个单独的 SO 帖子吗?
    • 我用我的代码创建了一个新帖子:stackoverflow.com/questions/52876207/…
    【解决方案4】:

    对于任何遇到计数器问题的人,即使没有待处理的请求,也不会再次达到零: 增加计数器时,我必须额外检查事件类型:

     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();
        }
      }
    ));
    

    【讨论】:

      猜你喜欢
      • 2022-06-16
      • 1970-01-01
      • 2023-04-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-11-06
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多