एंगुलर के साथ विश्व स्तर पर 401 का संचालन


90

अपने कोणीय 2 परियोजना में मैं उन सेवाओं से एपीआई कॉल करता हूं जो एक अवलोकन योग्य है। कॉलिंग कोड तब इस अवलोकन योग्य हो जाता है। उदाहरण के लिए:

getCampaigns(): Observable<Campaign[]> {
    return this.http.get('/campaigns').map(res => res.json());
}

मान लें कि सर्वर 401 लौटाता है। मैं इस त्रुटि को विश्व स्तर पर कैसे पकड़ सकता हूं और लॉगिन पृष्ठ / घटक पर पुनर्निर्देशित कर सकता हूं?

धन्यवाद।


यहाँ मेरे पास अभी तक क्या है:

// boot.ts

import {Http, XHRBackend, RequestOptions} from 'angular2/http';
import {CustomHttp} from './customhttp';

bootstrap(AppComponent, [HTTP_PROVIDERS, ROUTER_PROVIDERS,
    new Provider(Http, {
        useFactory: (backend: XHRBackend, defaultOptions: RequestOptions) => new CustomHttp(backend, defaultOptions),
        deps: [XHRBackend, RequestOptions]
    })
]);

// customhttp.ts

import {Http, ConnectionBackend, Request, RequestOptions, RequestOptionsArgs, Response} from 'angular2/http';
import {Observable} from 'rxjs/Observable';

@Injectable()
export class CustomHttp extends Http {
    constructor(backend: ConnectionBackend, defaultOptions: RequestOptions) {
        super(backend, defaultOptions);
    }

    request(url: string | Request, options?: RequestOptionsArgs): Observable<Response> {

        console.log('request...');

        return super.request(url, options);        
    }

    get(url: string, options?: RequestOptionsArgs): Observable<Response> {

        console.log('get...');

        return super.get(url, options);
    }
}

जो त्रुटि संदेश मुझे मिल रहा है वह "backend.createConnection एक फ़ंक्शन नहीं है"


1
मुझे लगता है कि यह आपको थोड़ा संकेत
पंकज पारकर

जवाबों:


78

विवरण

सबसे अच्छा समाधान मैंने पाया है XHRBackendकि इस तरह से ओवरराइड करने के लिए HTTP प्रतिक्रिया की स्थिति 401और 403एक विशेष कार्रवाई की ओर जाता है।

यदि आप अपने कोणीय अनुप्रयोग के बाहर अपने प्रमाणीकरण को संभालते हैं, तो आप वर्तमान पृष्ठ को ताज़ा करने के लिए बाध्य कर सकते हैं जैसे कि आपका बाहरी तंत्र चालू हो। मैं नीचे दिए गए कार्यान्वयन में इस समाधान का विवरण देता हूं।

आप अपने एप्लिकेशन के अंदर एक घटक को भी अग्रेषित कर सकते हैं जैसे कि आपका कोणीय अनुप्रयोग पुनः लोड नहीं किया जाता है।

कार्यान्वयन

कोणीय> 2.3.0

@Mrgoos के लिए धन्यवाद, यहाँ कोणीय 2.3.0+ में बग फिक्स के कारण सरलीकृत घोल 2.3.0+ है (देखें मुद्दा https://github.com/angular/angular/issues/11606 ) सीधे Httpमॉड्यूल का विस्तार ।

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


@Injectable()
export class AuthenticatedHttpService extends Http {

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

  request(url: string | Request, options?: RequestOptionsArgs): Observable<Response> {
    return super.request(url, options).catch((error: Response) => {
            if ((error.status === 401 || error.status === 403) && (window.location.href.match(/\?/g) || []).length < 2) {
                console.log('The authentication session expires or the user is not authorised. Force refresh of the current page.');
                window.location.href = window.location.href + '?' + new Date().getMilliseconds();
            }
            return Observable.throw(error);
        });
  }
}

मॉड्यूल फ़ाइल में अब केवल निम्न प्रदाता हैं।

providers: [
    { provide: Http, useClass: AuthenticatedHttpService }
]

राउटर और एक बाहरी प्रमाणीकरण सेवा का उपयोग करते हुए एक अन्य समाधान @ grgoos द्वारा निम्नलिखित gist में विस्तृत है ।

कोणीय पूर्व 2.3.0

निम्नलिखित कार्यान्वयन के लिए Angular 2.2.x FINALऔर काम करता है RxJS 5.0.0-beta.12

यदि HTTP कोड 401 या 403 वापस आ जाता है, तो यह वर्तमान पृष्ठ पर पुनर्निर्देशित करता है (साथ ही एक अद्वितीय URL प्राप्त करने और कैशिंग से बचने के लिए एक पैरामीटर)।

import { Request, XHRBackend, BrowserXhr, ResponseOptions, XSRFStrategy, Response } from '@angular/http';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/catch';
import 'rxjs/add/observable/throw';

export class AuthenticationConnectionBackend extends XHRBackend {

    constructor(_browserXhr: BrowserXhr, _baseResponseOptions: ResponseOptions, _xsrfStrategy: XSRFStrategy) {
        super(_browserXhr, _baseResponseOptions, _xsrfStrategy);
    }

    createConnection(request: Request) {
        let xhrConnection = super.createConnection(request);
        xhrConnection.response = xhrConnection.response.catch((error: Response) => {
            if ((error.status === 401 || error.status === 403) && (window.location.href.match(/\?/g) || []).length < 2) {
                console.log('The authentication session expires or the user is not authorised. Force refresh of the current page.');
                window.location.href = window.location.href + '?' + new Date().getMilliseconds();
            }
            return Observable.throw(error);
        });
        return xhrConnection;
    }

}

निम्न मॉड्यूल फ़ाइल के साथ।

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { HttpModule, XHRBackend } from '@angular/http';
import { AppComponent } from './app.component';
import { AuthenticationConnectionBackend } from './authenticated-connection.backend';

@NgModule({
    bootstrap: [AppComponent],
    declarations: [
        AppComponent,
    ],
    entryComponents: [AppComponent],
    imports: [
        BrowserModule,
        CommonModule,
        HttpModule,
    ],
    providers: [
        { provide: XHRBackend, useClass: AuthenticationConnectionBackend },
    ],
})
export class AppModule {
}

2
धन्यवाद! मुझे अपनी समस्या का पता चला ... मैं इस लाइन को याद कर रहा था, यही कारण है कि catch()नहीं मिला। (smh) import "rxjs/add/operator/catch";
hartpdx

1
क्या नेविगेशन करने के लिए राउटर मॉड्यूल का उपयोग करना संभव है?
युआनफेई झू

1
प्रामाणिक गार्ड के साथ बंडलिंग के लिए महान समाधान! 1. अधिकृत गार्ड अधिकृत उपयोगकर्ता की जाँच करता है (जैसे लोकलस्टोरेज में देख कर)। 2. 401/403 की प्रतिक्रिया पर आप गार्ड के लिए अधिकृत उपयोगकर्ता को साफ़ करें (जैसे कि लोकलस्टोरेज में कोरसिंग मापदंडों को हटाकर)। 3. जैसा कि इस प्रारंभिक चरण में आप प्रवेश पृष्ठ को अग्रेषित करने के लिए राउटर तक नहीं पहुंच सकते हैं, उसी पृष्ठ को रीफ़्रेश करके गार्ड चेक को ट्रिगर किया जाएगा, जो आपको लॉगिन स्क्रीन पर भेज देगा (और वैकल्पिक रूप से आपके प्रारंभिक URL को संरक्षित करेगा, इसलिए आप ' सफल प्रमाणीकरण के बाद अनुरोधित पृष्ठ पर भेज दिया जाएगा)।
एलेक्स क्लॉस

1
अरे @NicolasHenneaux - आपको क्यों लगता है कि यह बेहतर है तो ओवरराइड करना है http? मेरा एकमात्र लाभ यह है कि आप इसे केवल एक प्रदाता के रूप में रख सकते हैं: { provide: XHRBackend, useClass: AuthenticationConnectionBackend }जबकि एचटीपी को ओवरराइड करते समय आपको अधिक अजीब कोड लिखना होगा और useFactoryअपने आप को 'नया' कहकर और विशिष्ट तर्क भेजकर सीमित करना होगा। WDYT? दूसरी विधि का संदर्भ: adonespitogo.com/articles/angular-2-extending-http-provider
mrgoos

3
@ ब्रेट - मैंने इसके लिए एक जिस्ट बनाया है जो आपकी मदद करना चाहिए: gist.github.com/mrgoos/45ab013c2c044691b82d250a7df71e4c
mrgoos

82

कोणीय 4.3+

HttpClient की शुरुआत के साथ आसानी से सभी अनुरोधों / प्रतिक्रियाओं को अवरोधन करने की क्षमता आई। HttpInterceptors का सामान्य उपयोग अच्छी तरह से प्रलेखित है , मूल उपयोग और इंटरसेप्टर प्रदान करने के तरीके देखें। नीचे HttpInterceptor का एक उदाहरण है जो 401 त्रुटियों को संभाल सकता है।

RxJS 6+ के लिए अपडेट किया गया

import { Observable, throwError } from 'rxjs';
import { HttpErrorResponse, HttpEvent, HttpHandler,HttpInterceptor, HttpRequest } from '@angular/common/http';

import { Injectable } from '@angular/core';
import { catchError } from 'rxjs/operators';

@Injectable()
export class ErrorInterceptor implements HttpInterceptor {

  intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    return next.handle(req).pipe(
      catchError((err: HttpErrorResponse) => {
        if (err.status == 401) {
          // Handle 401 error
        } else {
          return throwError(err);
        }
      })
    );
  }

}

आरएक्सजेएस <6

import { Injectable } from '@angular/core';
import { HttpInterceptor, HttpRequest, HttpHandler, HttpEvent, HttpErrorResponse } from '@angular/common/http'
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/do';

@Injectable()
export class ErrorInterceptor implements HttpInterceptor {

    intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
        return next.handle(req).do(event => {}, err => {
            if (err instanceof HttpErrorResponse && err.status == 401) {
                // handle 401 errors
            }
        });
    }
}

1
क्या यह अभी भी आपके लिए काम कर रहा है? कल यह मेरे लिए काम कर रहा था, लेकिन अन्य मॉड्यूल स्थापित करने के बाद, मुझे यह त्रुटि मिल रही है: next.handle (…)
.do

मुझे लगता है कि यह एक वर्ग के विस्तार के रूप में इस्तेमाल किया जाना चाहिए जैसे http लगभग हमेशा एक गंध है
kboom

1
HTTP_INTERCEPTORS के साथ इसे अपने प्रदाताओं की सूची में जोड़ना न भूलें। आप डॉक्स
ब्रूनो पेरेस

2
महान लेकिन Routerयहाँ में काम करने के लिए प्रतीत नहीं होता है। उदाहरण के लिए, मैं अपने उपयोगकर्ताओं को पृष्ठ में लॉग इन करने के लिए रूट करना चाहता हूं जब उन्हें 401-403 मिलता है, लेकिन this.router.navigate(['/login']मेरे लिए काम नहीं कर रहा है। यह कुछ नहीं करता है
कोडीबगस्टीन

यदि आपको " import 'rxjs/add/operator/do';.do एक फ़ंक्शन नहीं है" मिल रहा है, तो rxjs आयात करने के बाद जोड़ें ।
amoss

19

जैसा कि फ्रंटएंड एपीआई दूध की तुलना में तेजी से समाप्त होता है, कोणीय 6+ और आरएक्सजेएस 5.5+ के साथ, आपको उपयोग करने की आवश्यकता है pipe:

import { HttpInterceptor, HttpEvent, HttpRequest, HttpHandler, HttpErrorResponse } from '@angular/common/http';
import { Observable, throwError } from 'rxjs';
import { Injectable } from '@angular/core';
import { catchError } from 'rxjs/operators';
import { Router } from '@angular/router';

@Injectable()
export class AuthInterceptor implements HttpInterceptor {

  constructor(private router: Router) { }

  intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    return next.handle(req).pipe(
      catchError((err: HttpErrorResponse) => {
        if (err.status === 401) {
          this.router.navigate(['login'], { queryParams: { returnUrl: req.url } });
        }
        return throwError(err);
      })
    );
  }
}

कोणीय 7+ और rxjs 6+ के लिए अपडेट करें

import { HttpRequest, HttpHandler, HttpEvent, HttpInterceptor, HttpErrorResponse } from '@angular/common/http';
import { Observable, of } from 'rxjs';
import { Injectable } from '@angular/core';
import { catchError } from 'rxjs/internal/operators';
import { Router } from '@angular/router';

@Injectable()
export class AuthInterceptor implements HttpInterceptor {

  constructor(private router: Router) { }

  intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    return next.handle(request)
      .pipe(
        catchError((err, caught: Observable<HttpEvent<any>>) => {
          if (err instanceof HttpErrorResponse && err.status == 401) {
            this.router.navigate(['login'], { queryParams: { returnUrl: request.url } });
            return of(err as any);
          }
          throw err;
        })
      );
  }
}

error TS2322: Type 'Observable<{}>' is not assignable to type 'Observable<HttpEvent<any>>'.जब मैं .pipeवहां होता हूं, तब कोई त्रुटि नहीं होती है जब मैं .pipe
ब्लैकिस

2
@BlackICE मुझे लगता है कि मेरे उत्तर में पहले वाक्य की पुष्टि करता है। मैंने नवीनतम संस्करण के लिए उत्तर के साथ अद्यतन किया है।
साब अमीनी

1
आपके एनजी 7 + उदाहरण reqमें वास्तव में है request- मेरे बनाने के लिए संपादन छोटा है
पूछा गया_

12

Observableआप प्रत्येक अनुरोध विधि से प्राप्त प्रकार का है Observable<Response>Responseवस्तु, एक है statusसंपत्ति जो आयोजन करेगा401 यदि सर्वर है कि कोड वापस लौटाया। इसलिए आप इसे मैप करने या इसे परिवर्तित करने से पहले पुनः प्राप्त करना चाह सकते हैं।

यदि आप प्रत्येक कॉल पर इस कार्यक्षमता को करने से बचना चाहते हैं, तो आपको कोणीय 2 के Httpवर्ग का विस्तार करना होगा और इसे अपने स्वयं के कार्यान्वयन को इंजेक्ट करना होगा जो superनियमित Httpकार्यक्षमता के लिए अभिभावक ( ) को कॉल करता है और फिर संभालता है।401 ऑब्जेक्ट को वापस करने से पहले त्रुटि को ।

देख:

https://angular.io/docs/ts/latest/api/http/index/Response-class.html


इसलिए यदि मैं Http का विस्तार करता हूं तो मुझे Http के भीतर से "लॉगिन" मार्ग पर पुनर्निर्देशित करने में सक्षम होना चाहिए?
pbz

यही सिद्धांत है। आपको इसे करने के लिए अपने Http कार्यान्वयन में राउटर को इंजेक्ट करना होगा।
लैंगली

आपकी सहायता के लिए धन्यवाद। मैंने प्रश्न को एक नमूना कोड के साथ अद्यतन किया है। मैं शायद कुछ गलत कर रहा हूं (कोणीय के लिए नया)। क्या कोई विचार है की यह क्या हो सकता है? धन्यवाद।
pbz

आप डिफ़ॉल्ट Http प्रदाताओं का उपयोग कर रहे हैं, आपको अपना स्वयं का प्रदाता बनाना होगा जो कि डिफ़ॉल्ट के बजाय आपकी कक्षा के एक उदाहरण के लिए हल होता है। देखें: angular.io/docs/ts/latest/api/core/Provider-class.html
Langley

1
@ लैंग्ली, धन्यवाद। आप सही हैं: सदस्यता ((परिणाम) => {}, (त्रुटि) => {कंसोल.लॉग (त्रुटि.स्टैट));}} त्रुटि पैरामीटर अभी भी प्रकार की प्रतिक्रिया है।
abedurftig

9

कोणीय 4.3+

गिल्बर्ट एरेनास डैगर जवाब को पूरा करने के लिए :

अगर आपको किसी भी त्रुटि को रोकने की आवश्यकता है, तो इसके लिए एक उपचार लागू करें और इसे श्रृंखला के नीचे अग्रेषित करें (और इसके साथ केवल एक साइड इफेक्ट जोड़ें .do), आप किसी तरह का कुछ करने के लिए HttpClient और इसके इंटरसेप्टर्स का उपयोग कर सकते हैं :

import { HttpErrorResponse, HttpEvent, HttpHandler, HttpInterceptor, HttpRequest } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/catch';

@Injectable()
export class ErrorInterceptor implements HttpInterceptor {
    intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
        // install an error handler
        return next.handle(req).catch((err: HttpErrorResponse) => {
            console.log(err);
            if (err.error instanceof Error) {
                // A client-side or network error occurred. Handle it accordingly.
                console.log('An error occurred:', err.error.message);
            } else {
                // The backend returned an unsuccessful response code.
                // The response body may contain clues as to what went wrong,
                console.log(`Backend returned code ${err.status}, body was: ${err.error}`);
            }

            return Observable.throw(new Error('Your custom error'));
        });
    }
}

9

"राउटर" जैसी सेवाओं के एचटीटीपी व्युत्पन्न वर्ग में इंजेक्ट होने के कारण होने वाले चक्रीय संदर्भ मुद्दे से बचने के लिए, एक को पोस्ट-कंस्ट्रक्टर इंजेक्टर विधि का उपयोग करना चाहिए। निम्न कोड एक Http सेवा का एक कार्यशील कार्यान्वयन है जो हर बार लॉगिन मार्ग पर पुनर्निर्देशित करता है जब REST API "Token_Expired" देता है। ध्यान दें कि इसे नियमित Http के प्रतिस्थापन के रूप में उपयोग किया जा सकता है और इस तरह, आपके एप्लिकेशन के पहले से मौजूद घटकों या सेवाओं में कुछ भी बदलने की आवश्यकता नहीं है।

app.module.ts

  providers: [  
    {provide: Http, useClass: ExtendedHttpService },
    AuthService,
    PartService,
    AuthGuard
  ],

विस्तारित-http.service.ts

import { Injectable, Injector } from '@angular/core';
import { Request, XHRBackend, RequestOptions, Response, Http, RequestOptionsArgs, Headers } from '@angular/http';
import { Observable } from 'rxjs/Observable';
import { Router } from '@angular/router';
import { AuthService } from './auth.service';
import 'rxjs/add/operator/catch';
import 'rxjs/add/observable/throw';

@Injectable()
export class ExtendedHttpService extends Http {
    private router; 
    private authService;

  constructor(  backend: XHRBackend, defaultOptions: RequestOptions, private injector: Injector) {
    super(backend, defaultOptions);
  }

  request(url: string | Request, options?: RequestOptionsArgs): Observable<Response> {
 
    if (typeof url === 'string') {
      if (!options) {
        options = { headers: new Headers() };
      }
      this.setHeaders(options);
    } else {
      this.setHeaders(url);
    }
    console.log("url: " + JSON.stringify(url) +", Options:" + options);

    return super.request(url, options).catch(this.catchErrors());
  }

  private catchErrors() {

    return (res: Response) => {
        if (this.router == null) {
            this.router = this.injector.get(Router);
        }
        if (res.status === 401 || res.status === 403) {
            //handle authorization errors
            //in this example I am navigating to login.
            console.log("Error_Token_Expired: redirecting to login.");
            this.router.navigate(['signin']);
        }
        return Observable.throw(res);
    };
  }

  private setHeaders(objectToSetHeadersTo: Request | RequestOptionsArgs) {
      
      if (this.authService == null) {
            this.authService = this.injector.get(AuthService);
      }
    //add whatever header that you need to every request
    //in this example I could set the header token by using authService that I've created
     //objectToSetHeadersTo.headers.set('token', this.authService.getToken());
  }
}


8

कोणीय> = 2.3.0 से आप ओवरराइड कर सकते हैंHTTP मॉड्यूल और अपनी सेवाओं को इंजेक्ट कर सकते हैं। 2.3.0 संस्करण से पहले, आप एक कोर बग के कारण अपनी इंजेक्टेड सेवाओं का उपयोग नहीं कर सकते थे।

मैंने यह दिखाने के लिए एक जिस्ट बनाया है कि यह कैसे किया जाता है।


इसे एक साथ रखने के लिए धन्यवाद। मुझे एक बिल्ड त्रुटि मिल रही थी जिसमें कहा गया था "app.module.ts में" Http 'नाम नहीं मिल सकता है, इसलिए मैंने आयात किया और मुझे अब निम्न त्रुटि मिल रही है: "Ng चकमूले AppModule में त्वरित रूप से चक्रीय निर्भरता नहीं कर सकता"
ब्रायन

अरे @ ब्रेट- क्या आप अपना app.moduleकोड साझा कर सकते हैं ? धन्यवाद।
mrgoos

यह ठीक लगता है। क्या आप विस्तारित HTTP को जोड़ सकते हैं? इसके अलावा, क्या आप HTTPकहीं और आयात करते हैं ?
मर्गियो डेस

विलंब के लिए क्षमा चाहते हैं। मैं अब Angular 2.4 पर हूं और वही त्रुटि प्राप्त कर रहा हूं। मैं कई फ़ाइलों में Http आयात करता हूं। यहाँ मेरा अद्यतन किया गया है: gist.github.com/anonymous/606d092cac5b0eb7f48c9a357cd150c3
ब्रायन

यहाँ एक ही मुद्दा ... ऐसा लगता है कि यह काम नहीं कर रहा है इसलिए शायद हमें इसे इस तरह चिह्नित करना चाहिए?
टूथमोसिस

2

कोणीय> 4.3: आधार सेवा के लिए एररहैंडलर

protected handleError(err: HttpErrorResponse | any) {
    console.log('Error global service');
    console.log(err);
    let errorMessage: string = '';

    if (err.hasOwnProperty('status')) { // if error has status
        if (environment.httpErrors.hasOwnProperty(err.status)) {
            // predefined errors
            errorMessage = environment.httpErrors[err.status].msg; 
        } else {
            errorMessage = `Error status: ${err.status}`;
            if (err.hasOwnProperty('message')) {
                errorMessage += err.message;
            }
        }
     }

    if (errorMessage === '') {
        if (err.hasOwnProperty('error') && err.error.hasOwnProperty('message')) { 
            // if error has status
            errorMessage = `Error: ${err.error.message}`;
        }
     }

    // no errors, then is connection error
    if (errorMessage === '') errorMessage = environment.httpErrors[0].msg; 

    // this.snackBar.open(errorMessage, 'Close', { duration: 5000 }});
    console.error(errorMessage);
    return Observable.throw(errorMessage);
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.