web-dev-qa-db-fra.com

Comment étendre la classe angulaire 2 http en Angular 2 final

J'essaie d'étendre la classe angulaire 2 http pour pouvoir gérer les erreurs globales et configurer des en-têtes pour mon service secureHttp. J'ai trouvé des solutions mais cela ne fonctionne pas avec la version finale de Angular 2 . Voici mon code: 

Fichier: secureHttp.service.ts

import { Injectable } from '@angular/core';
import { Http, ConnectionBackend, Headers, RequestOptions, Response, RequestOptionsArgs} from '@angular/http';

@Injectable()
export class SecureHttpService extends Http {

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

Fichier: app.module.ts

    import { BrowserModule, Title } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { routing } from './app.routes';
import { AppComponent } from './app.component';
import { HttpModule, Http, XHRBackend, RequestOptions } from '@angular/http';
import { CoreModule } from './core/core.module';
import {SecureHttpService} from './config/secure-http.service'

@NgModule({
  declarations: [
    AppComponent,
  ],
  imports: [
    BrowserModule,
    CoreModule,
    routing,
    HttpModule,
  ],
  providers: [
    {
      provide: Http,
      useFactory: (backend: XHRBackend, defaultOptions: RequestOptions) => {
        return new SecureHttpService(backend, defaultOptions);
      },
      deps: [ XHRBackend, RequestOptions]
    }, Title, SecureHttpService],
  bootstrap: [AppComponent],
})
export class AppModule { }

composant.ts

constructor(private titleService: Title, private _secure: SecureHttpService) {}

  ngOnInit() {
    this.titleService.setTitle('Dashboard');
    this._secure.get('http://api.example.local')
        .map(res => res.json())
        .subscribe(
            data =>  console.log(data) ,
            err => console.log(err),
            () => console.log('Request Complete')
        );
  }

Pour l'instant, cela me renvoie une erreur "Aucun fournisseur pour ConnectionBackend!" . Merci de l'aide!

24
mezhik91

La raison de l’erreur est que vous essayez de fournir SecureHttpService

providers: [SecureHttpService]

Cela signifie qu'Angular essaiera de créer l'instance, not à l'aide de votre usine. Et il n'a pas de fournisseur enregistré avec le jeton ConnectionBackend à donner à votre constructeur.

Vous pouvez supprimer la SecureHttpService de la providers, mais cela vous donnera une autre erreur (ce qui, je suppose, est la raison pour laquelle vous l'avez ajouté en premier lieu). L'erreur sera quelque chose comme "aucun fournisseur pour SecureHttpService" parce que vous essayez de l'injecter dans votre constructeur

constructor(private titleService: Title, private _secure: SecureHttpService) {}

Voici où vous devez comprendre les jetons. La valeur que vous fournissez à provide est le token.

{
  provide: Http,
  useFactory: ()
}

Le jeton est ce que nous sommes autorisés à injecter. Donc, vous pouvez plutôt injecter la Http et il utilisera votre créé SecureHttpService. Mais cela vous enlèvera toute chance que vous utilisiez la constante Http, si vous en avez besoin.

constructor(private titleService: Title, private _secure: Http) {}

Si vous n'avez besoin de rien savoir sur la variable SecureHttpService, vous pouvez le laisser comme ceci.

Si vous voulez être capable d’injecter le type SecureHttpService (vous avez peut-être besoin d’une API, ou voulez utiliser la normale Http ailleurs), changez simplement la provide

{
  provide: SecureHttpService,
  useFactory: ()
}

Maintenant, vous pouvez injecter à la fois la Http régulière et votre SecureHttpService. Et n'oubliez pas de supprimer la SecureHttpService de la providers.

22
Paul Samsotha

Découvrez mon article sur comment étendre la classe Http pour Angular 2.1.1

Commençons par créer notre classe de fournisseur http personnalisée.

http.service.ts

import {Injectable} from '@angular/core';
import {Http, XHRBackend, RequestOptions, Request, RequestOptionsArgs, Response, Headers} from '@angular/http';
import {Observable} from 'rxjs/Observable';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/catch';

@Injectable()
export class HttpService extends Http {

  constructor (backend: XHRBackend, options: RequestOptions) {
    let token = localStorage.getItem('auth_token'); // your custom token getter function here
    options.headers.set('Authorization', `Bearer ${token}`);
    super(backend, options);
  }

  request(url: string|Request, options?: RequestOptionsArgs): Observable<Response> {
    let token = localStorage.getItem('auth_token');
    if (typeof url === 'string') { // meaning we have to add the token to the options, not in url
      if (!options) {
        // let's make option object
        options = {headers: new Headers()};
      }
      options.headers.set('Authorization', `Bearer ${token}`);
    } else {
    // we have to add the token to the url object
      url.headers.set('Authorization', `Bearer ${token}`);
    }
    return super.request(url, options).catch(this.catchAuthError(this));
  }

  private catchAuthError (self: HttpService) {
    // we have to pass HttpService's own instance here as `self`
    return (res: Response) => {
      console.log(res);
      if (res.status === 401 || res.status === 403) {
        // if not authenticated
        console.log(res);
      }
      return Observable.throw(res);
    };
  }
}

Maintenant, nous devons configurer notre module principal pour fournir le XHRBackend à notre classe http personnalisée. Dans votre déclaration de module principal, ajoutez ce qui suit au tableau de fournisseurs:

app.module.ts

import { HttpModule, RequestOptions, XHRBackend } from '@angular/http';
import { HttpService } from './services/http.service';
...
@NgModule({
  imports: [..],
  providers: [
    {
      provide: HttpService,
      useFactory: (backend: XHRBackend, options: RequestOptions) => {
        return new HttpService(backend, options);
      },
      deps: [XHRBackend, RequestOptions]
    }
  ],
  bootstrap: [ AppComponent ]
})

Après cela, vous pouvez maintenant utiliser votre fournisseur http personnalisé dans vos services. Par exemple:

user.service.ts

import { Injectable }     from '@angular/core';
import {HttpService} from './http.service';

@Injectable()
class UserService {
  constructor (private http: HttpService) {}

  // token will added automatically to get request header
  getUser (id: number) {
    return this.http.get(`/users/${id}`).map((res) => {
      return res.json();
    } );
  }
}
21
Adones Pitogo

Je pense que la réponse de peeskillet devrait être la réponse choisie. Ce que je veux dire ici ne fait que renforcer sa réponse et non la concurrencer, mais je voulais aussi donner un exemple concret, car je ne pense pas Il est évident à 100% que le code de peeskillet correspond à la réponse.

Je mets ce qui suit dans la section providers de mon app.module.ts. J'appelle ma coutume Http replacement MyHttp.

Remarquez comment, comme dit Peeskillet, ce serait provide: Http, pas provide: MyHttp.

  providers: [
    AUTH_PROVIDERS
    {
      provide: Http,
      useFactory: (backend: XHRBackend, defaultOptions: RequestOptions) => {
        return new MyHttp(backend, defaultOptions);
      },
      deps: [XHRBackend, RequestOptions]
    }
  ],

Ensuite, ma classe Http- extend est définie comme suit:

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

@Injectable()
export class MyHttp extends Http {
  get(url: string, options?: any) {
    // This is pointless but you get the idea
    console.log('MyHttp');
    return super.get(url, options);
  }
}

Rien de spécial ne doit être fait pour que votre application utilise MyHttp au lieu de Http.

3
Jason Swett

Depuis Angular 4.3, nous n’avons plus besoin de extends http. Au lieu de cela, nous pouvons utiliser HttpInterceptor et HttpClient pour archiver tous ces éléments.

C'est similaire et plus facile que d'utiliser Http.

J'ai migré vers HttpClient en environ 2 heures.

Le détail est ici

2
Frank Nguyen

Vous pouvez en fait simplement étendre le Http dans votre propre classe, puis utiliser une fabrique personnalisée pour fournir un Http comme suit:

puis, dans mes fournisseurs d’applications, j’ai pu utiliser une fabrique personnalisée pour fournir un "Http"

importer {RequestOptions, Http, XHRBackend} depuis '@ angular/http';

class HttpClient extends Http {
 /*
  insert your extended logic here. In my case I override request to
  always add my access token to the headers, then I just call the super 
 */
  request(req: string|Request, options?: RequestOptionsArgs): Observable<Response> {

      options = this._setCustomHeaders(options);
      // Note this does not take into account where req is a url string
      return super.request(new Request(mergeOptions(this._defaultOptions,options, req.method, req.url)))
    }

  }
}

function httpClientFactory(xhrBackend: XHRBackend, requestOptions: RequestOptions): Http {

  return new HttpClient(xhrBackend, requestOptions);
}

@NgModule({
  imports:[
    FormsModule,
    BrowserModule,
  ],
  declarations: APP_DECLARATIONS,
  bootstrap:[AppComponent],
  providers:[
     { provide: Http, useFactory: httpClientFactory, deps: [XHRBackend, RequestOptions]}
  ],
})
export class AppModule {
  constructor(){

  }
}

avec cette approche, vous n'avez pas besoin de remplacer l'une quelconque des fonctions HTTP que vous ne souhaitez pas modifier.

0
jonnie

Vous pouvez vérifier https://www.illucit.com/blog/2016/03/angular2-http-authentication-interceptor/ qui vous aidera.

Changez vos fournisseurs comme ci-dessous pour la dernière version et vérifiez-la:

providers: [
  {
    provide: SecureHttpService,
    useFactory: (backend: XHRBackend, defaultOptions: RequestOptions) => {
      return new SecureHttpService(backend, defaultOptions);
    },
    deps: [ XHRBackend, RequestOptions]
  },
  Title
]
0
ranakrunal9