Angular 4 - HTTP Interceptor

2020-01-29 05:40发布

I'm trying to create HTTP Interceptor in Angular 4 but I'm having some weird error. Following is my error:

Argument of type 'Observable<Response>' is not assignable to parameter of type 'Observable<Response>'.

Following is my Code:

import { Injectable } from '@angular/core';
import { Http, ConnectionBackend, RequestOptions, RequestOptionsArgs } from '@angular/http';
import { Router } from '@angular/router';
import { Observable } from 'rxjs/Observable';
import * as _ from 'lodash';

@Injectable()
export class HttpInterceptor extends Http {

    constructor(backend: ConnectionBackend, defaultOptions: RequestOptions, private _router: Router) {
        super(backend, defaultOptions);
    }

    request(url: string | Request, options?: RequestOptionsArgs): Observable<Response> {
        return this.intercept(super.request(url, options)); // Here is the error
    }

    get(url: string, options?: RequestOptionsArgs): Observable<Response> {
        return this.intercept(super.get(url, options)); // Here is the error
    }

    post(url: string, body: string, options?: RequestOptionsArgs): Observable<Response> {
        return this.intercept(super.post(url, body, this.getRequestOptionArgs(options))); // Here is the error
    }

    put(url: string, body: string, options?: RequestOptionsArgs): Observable<Response> {
        return this.intercept(super.put(url, body, this.getRequestOptionArgs(options))); // Here is the error
    }

    delete(url: string, options?: RequestOptionsArgs): Observable<Response> {
        return this.intercept(super.delete(url, options)); // Here is the error
    }

    getRequestOptionArgs(options?: RequestOptionsArgs): RequestOptionsArgs {
        if (options == null) {
            options = new RequestOptions();
        }
        if (options.headers == null) {
            options.headers = new Headers(); // Here is the error
        }
        options.headers.append('Content-Type', 'application/json');
        return options;
    }

    intercept(observable: Observable<Response>): Observable<Response> {
        return observable.catch((err, source) => {
            if (err.status == 401 && !_.endsWith(err.url, 'api/auth/login')) {
                this._router.navigate(['/login']);
                return Observable.empty();
            } else {
                return Observable.throw(err);
            }
        });

    }

}

Does anyone know what is going wrong here? I tried debugging for 3 hours but unable to find any clue.

Edit:

I also tried to remove everything and written a code like this:

request(url: string | Request, options?: RequestOptionsArgs): Observable<Response> {
    return super.request(url, options);
}

But still it's giving same error:

Argument of type 'string | Request' is not assignable to parameter of type 'string | Request'. Type 'Request' is not assignable to type 'string | Request'.

标签: angular
5条回答
Anthone
2楼-- · 2020-01-29 06:02

Use this logic to intercept Ajax in any framework.

private bootstrapAjaxInterceptor() {
const _self = this;
const originalOpen = XMLHttpRequest.prototype.open;
XMLHttpRequest.prototype.open = function (xhrMethod, requestUrl) {
  this.addEventListener('readystatechange', xhr => {
    switch (this.readyState) {
      case 1: _self.onAjaxStart(xhrMethod, requestUrl); break;
      case 4: _self.onAjaxStop(this.responseURL, this.response); break;
      default: // Nothing to handle here
    }
  }, false);
  originalOpen.apply(this, arguments);
};

}

Here is complete example for Angular.

import { Injectable } from '@angular/core';
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
interface AjaxRequest {
  url?: string;
  requestCount?: number;
  method?: string;
}
interface AjaxResponse {
  url?: string;
  requestCount?: number;
  response?: string;
}
@Injectable()
export class HttpInterceptor {
  public ajaxStart = new BehaviorSubject<AjaxRequest>({});
  public ajaxStop = new BehaviorSubject<AjaxResponse>({});
  private requestQueue: Array<any> = [];
  constructor() {
    this.bootstrapAjaxInterceptor();
  }
  public getRequestQueue() {
    return this.requestQueue;
  }
  private bootstrapAjaxInterceptor() {
    const _self = this;
    const originalOpen = XMLHttpRequest.prototype.open;
    XMLHttpRequest.prototype.open = function (xhrMethod, requestUrl) {
      this.addEventListener('readystatechange', xhr => {
        switch (this.readyState) {
          case 1: _self.onAjaxStart(xhrMethod, requestUrl); break;
          case 4: _self.onAjaxStop(this.responseURL, this.response); break;
          default: // Nothing to handle here
        }
      }, false);
      originalOpen.apply(this, arguments);
    };
  }
  onAjaxStart(xhrMethod, requestUrl) {
    this.requestQueue.push(requestUrl.replace(/\?.*/, ''));
    this.ajaxStart.next({
      url: requestUrl,
      requestCount: this.requestQueue.length,
      method: xhrMethod
    });
  }
  onAjaxStop(responseURL, response) {
    const responseUrl = responseURL.split(/\?/)[0];
    this.requestQueue.forEach((urlEndpoint, i) => {
      if (new RegExp(`${urlEndpoint}$`).test(responseUrl)) {
        return this.requestQueue.splice(i, 1);
      }
    });
    this.ajaxStop.next({
      url: responseUrl,
      requestCount: this.requestQueue.length,
      response: response
    });
  }
}
查看更多
Explosion°爆炸
3楼-- · 2020-01-29 06:15

Http interceptor is already implemented in Angular 4.3.4 and is described in the documentation.

You need to implement the intercept method of HttpInterceptor interface, do something with the request, and call the next.handle(req) method.

import {Injectable} from '@angular/core';
import {HttpEvent, HttpInterceptor, HttpHandler, HttpRequest} from '@angular/common/http';

@Injectable()
export class NoopInterceptor implements HttpInterceptor {
  intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    const changedReq = req.clone({headers: req.headers.set('My-Header', 'MyHeaderValue')});
    return next.handle(changedReq);
  }
}

It's also necessary to register interceptor in app's providers section

import {NgModule} from '@angular/core';
import {HTTP_INTERCEPTORS} from '@angular/common/http';

@NgModule({
  providers: [{
    provide: HTTP_INTERCEPTORS,
    useClass: NoopInterceptor,
    multi: true,
  }],
})
export class AppModule {}
查看更多
我想做一个坏孩纸
4楼-- · 2020-01-29 06:23

The following instruction worked for me perfectly:

In the app module:

import { HTTP_INTERCEPTORS, HttpClientModule } from '@angular/common/http';
import { myInterceptor} from './Interceptors/myInterceptor';

@NgModule({
    imports: [
...
    HttpClientModule,
  ],
  providers: [{
    provide: HTTP_INTERCEPTORS,
    useClass: MyInterceptor,
    multi: true
  }],
  bootstrap: [AppComponent]
})

In the interceptor:

import { Injectable } from '@angular/core';

import {
  HttpEvent, HttpInterceptor, HttpHandler, HttpRequest, HttpResponse
} from '@angular/common/http';

import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/do';
import { HttpErrorResponse } from '@angular/common/http';
import { RequestOptions } from '@angular/http';
import { HttpHeaders } from '@angular/common/http';

@Injectable()
export class MyInterceptor implements HttpInterceptor {
  intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    // when there is POST request
    if (req.method === 'POST') {
      const content_type = 'application/x-www-form-urlencoded';
      const req= req.clone({
        headers: req.headers.set('Content-Type', content_type),
        body: 'my body'
      });

      return next.handle(accessReq);
    }
  }
}

IMPORTANT: http should be an instance of HttpClient!

constructor(
    private http: HttpClient, private accessTokenService: AccessTokenService
) { }
    return this.http.post(ingestURL, null)
        .map(res => {
            return res; // data returned from interceptor
        });
查看更多
别忘想泡老子
5楼-- · 2020-01-29 06:24

The globally available DOM typings ("lib": ["dom"] in your tsconfig) include Response and Request interfaces that are unrelated to the types used by Angular.

You need to import Response and Request from @angular/http.

查看更多
闹够了就滚
6楼-- · 2020-01-29 06:27

akn's answer is the right one. I was trying to make it work with http service, but the interceptor only works whit httpClient service.

import { HttpClient } from '@angular/common/http';

constructor(private http: HttpClient) { }
查看更多
登录 后发表回答