Dalam proyek Angular 2 saya, saya membuat panggilan API dari layanan yang mengembalikan Observable. Kode panggilan kemudian berlangganan observable ini. Sebagai contoh:
getCampaigns(): Observable<Campaign[]> {
return this.http.get('/campaigns').map(res => res.json());
}
Katakanlah server mengembalikan 401. Bagaimana saya bisa menangkap kesalahan ini secara global dan mengarahkan ke halaman / komponen login?
Terima kasih.
Inilah yang saya miliki sejauh ini:
// 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);
}
}
Pesan kesalahan yang saya dapatkan adalah "backend.createConnection bukan fungsi"
Jawaban:
Deskripsi
Solusi terbaik yang saya temukan adalah menimpa
XHRBackend
status respons HTTP401
dan403
mengarah ke tindakan tertentu.Jika Anda menangani otentikasi Anda di luar aplikasi Angular Anda, Anda dapat memaksa menyegarkan halaman saat ini sehingga mekanisme eksternal Anda dipicu. Saya merinci solusi ini dalam implementasi di bawah ini.
Anda juga bisa meneruskan ke komponen di dalam aplikasi Anda sehingga aplikasi Angular Anda tidak dimuat ulang.
Penerapan
Sudut> 2.3.0
Berkat @mrgoos, berikut adalah solusi yang disederhanakan untuk angular 2.3.0+ karena perbaikan bug di angular 2.3.0 (lihat masalah https://github.com/angular/angular/issues/11606 ) memperluas
Http
modul secara langsung .File modul sekarang hanya berisi penyedia berikut.
Solusi lain yang menggunakan Router dan layanan otentikasi eksternal dirinci dalam inti berikut oleh @mrgoos.
Angular sebelum 2.3.0
Implementasi berikut bekerja untuk
Angular 2.2.x FINAL
danRxJS 5.0.0-beta.12
.Ini mengalihkan ke halaman saat ini (ditambah parameter untuk mendapatkan URL unik dan menghindari caching) jika kode HTTP 401 atau 403 dikembalikan.
dengan file modul berikut.
sumber
catch()
tidak ditemukan. (smh)import "rxjs/add/operator/catch";
http
? Satu-satunya keuntungan yang saya lihat adalah Anda dapat dengan mudah menempatkannya sebagai penyedia:{ provide: XHRBackend, useClass: AuthenticationConnectionBackend }
sementara saat menimpa Http Anda perlu menulis kode yang lebih canggung sepertiuseFactory
dan membatasi diri Anda dengan memanggil 'baru' dan mengirimkan argumen khusus. WDYT? Referensi ke metode ke-2: adonespitogo.com/articles/angular-2-extending-http-providerAngular 4.3+
Dengan diperkenalkannya HttpClient muncul kemampuan untuk dengan mudah mencegat semua permintaan / tanggapan. Penggunaan umum HttpInterceptors didokumentasikan dengan baik , lihat penggunaan dasar dan cara menyediakan interseptor. Di bawah ini adalah contoh HttpInterceptor yang dapat menangani kesalahan 401.
Diperbarui untuk RxJS 6+
RxJS <6
sumber
Router
di sini sepertinya tidak berhasil. Misalnya, saya ingin mengarahkan pengguna saya ke halaman masuk saat mereka mendapatkan 401-403, tetapithis.router.navigate(['/login']
tidak berfungsi untuk saya. Itu tidak melakukan apaimport 'rxjs/add/operator/do';
setelah Anda mengimpor rxjs.Karena API frontend kedaluwarsa lebih cepat daripada susu, dengan Angular 6+ dan RxJS 5.5+, Anda perlu menggunakan
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); }) ); } }
Pembaruan untuk Angular 7+ dan 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; }) ); } }
sumber
error TS2322: Type 'Observable<{}>' is not assignable to type 'Observable<HttpEvent<any>>'.
ketika.pipe
ada di sana, tidak ada kesalahan ketika saya menghapus.pipe
req
sebenarnyarequest
- pengeditan terlalu kecil untuk saya buatYang
Observable
Anda dapatkan dari setiap metode permintaan adalah tipeObservable<Response>
. TheResponse
objek, memilikistatus
properti yang akan memegang401
JIKA server kembali kode tersebut. Jadi, Anda mungkin ingin mengambilnya sebelum memetakan atau mengonversinya.Jika Anda ingin menghindari melakukan fungsionalitas ini pada setiap panggilan, Anda mungkin harus memperluas
Http
kelas Angular 2 dan memasukkan implementasi Anda sendiri yang memanggil induk (super
) untukHttp
fungsionalitas reguler dan kemudian menangani401
kesalahan sebelum mengembalikan objek.Lihat:
https://angular.io/docs/ts/latest/api/http/index/Response-class.html
sumber
Angular 4.3+
Untuk melengkapi jawaban The Gilbert Arenas Dagger :
Jika yang Anda butuhkan adalah mencegat kesalahan apa pun, terapkan perawatan untuk itu dan teruskan ke bawah rantai (dan tidak hanya menambahkan efek samping dengan
.do
), Anda dapat menggunakan HttpClient dan interseptornya untuk melakukan hal semacam itu:sumber
Untuk menghindari masalah referensi siklik yang disebabkan oleh layanan seperti "Router" yang dimasukkan ke dalam kelas turunan Http, seseorang harus menggunakan metode Injector pasca-konstruktor. Kode berikut adalah implementasi yang berfungsi dari layanan Http yang mengarahkan ke rute Login setiap kali REST API mengembalikan "Token_Expired". Perhatikan bahwa ini bisa digunakan sebagai pengganti Http biasa dan dengan demikian, tidak perlu mengubah apa pun dalam komponen atau layanan aplikasi Anda yang sudah ada.
app.module.ts
providers: [ {provide: Http, useClass: ExtendedHttpService }, AuthService, PartService, AuthGuard ],
extended-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()); } }
sumber
Dari Angular> = 2.3.0 Anda dapat mengganti
HTTP
modul dan memasukkan layanan Anda. Sebelum versi 2.3.0, Anda tidak dapat menggunakan layanan yang diinjeksi karena bug inti.Saya telah membuat inti untuk menunjukkan bagaimana hal itu dilakukan.
sumber
app.module
kode Anda ? Terima kasih.HTTP
tempat lain?Angular> 4.3: ErrorHandler untuk layanan dasar
sumber