В моем проекте Angular 2 я делаю вызовы API из служб, которые возвращают Observable. Затем вызывающий код подписывается на эту наблюдаемую. Например:
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 не является функцией».
Ответы:
Описание
Лучшее решение, которое я нашел, - переопределить
XHRBackend
так, чтобы статус ответа HTTP401
и403
приводил к определенному действию.Если вы обрабатываете аутентификацию вне приложения Angular, вы можете принудительно обновить текущую страницу, чтобы сработал ваш внешний механизм. Я подробно описываю это решение в реализации ниже.
Вы также можете перенаправить компонент внутри вашего приложения, чтобы ваше приложение Angular не перезагружалось.
Реализация
Угловой> 2.3.0
Благодаря @mrgoos, вот упрощенное решение для angular 2.3.0+ из-за исправления ошибки в angular 2.3.0 (см. Проблему https://github.com/angular/angular/issues/11606 ), расширяющую непосредственно
Http
модуль.Теперь файл модуля содержит только следующего провайдера.
Другое решение, использующее маршрутизатор и внешнюю службу аутентификации, подробно описывается в следующей сущности. по @mrgoos.
Угловой до 2.3.0
Следующая реализация работает для
Angular 2.2.x FINAL
иRxJS 5.0.0-beta.12
.Он перенаправляет на текущую страницу (плюс параметр для получения уникального URL-адреса и предотвращения кеширования), если возвращается HTTP-код 401 или 403.
со следующим файлом модуля.
источник
catch()
она не была найдена. (smh)import "rxjs/add/operator/catch";
http
? Единственное преимущество, которое я вижу, это то, что вы можете просто указать его как поставщика:{ provide: XHRBackend, useClass: AuthenticationConnectionBackend }
при переопределении Http вам нужно писать более неудобный код, например,useFactory
и ограничивать себя, вызывая 'new' и отправляя определенные аргументы. WDYT? Ссылка на 2-й метод: adonespitogo.com/articles/angular-2-exnding-http-providerУгловой 4.3+
С появлением HttpClient появилась возможность легко перехватывать все запросы / ответы. Общее использование HttpInterceptors хорошо задокументировано , см. Основные принципы использования и способы предоставления перехватчика. Ниже приведен пример HttpInterceptor, который может обрабатывать ошибку 401.
Обновлено для RxJS 6+
RxJS <6
источник
Router
здесь, похоже, не работает. Например, я хочу направить своих пользователей на страницу входа в систему, когда они получат 401-403, ноthis.router.navigate(['/login']
у меня это не работает. Это ничего не делаетimport 'rxjs/add/operator/do';
после импорта rxjs.Поскольку интерфейсы API-интерфейсов истекают быстрее, чем молоко, с Angular 6+ и RxJS 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); }) ); } }
Обновление для Angular 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
req
это на самом делеrequest
- редактированиеObservable
Вы получаете от каждого метода запроса имеет типаObservable<Response>
. УResponse
объекта естьstatus
свойство, которое будет содержать,401
если сервер вернул этот код. Так что вы можете получить это перед отображением или преобразованием.Если вы хотите избежать использования этой функции при каждом вызове, вам, возможно, придется расширить
Http
класс Angular 2 и внедрить свою собственную реализацию, которая вызывает parent (super
) для обычныхHttp
функций, а затем обрабатывает401
ошибку перед возвратом объекта.Увидеть:
https://angular.io/docs/ts/latest/api/http/index/Response-class.html
источник
Угловой 4.3+
Завершить Кинжал Гилберта Аренаса ответ :
Если вам нужно перехватить любую ошибку, применить к ней обработку и передать ее по цепочке (а не просто добавить побочный эффект с помощью
.do
), вы можете использовать HttpClient и его перехватчики, чтобы сделать что-то в этом роде:источник
Чтобы избежать проблемы с циклическими ссылками, которая возникает из-за того, что такие службы, как «Маршрутизатор», внедряются в производный класс Http, необходимо использовать метод Injector после конструктора. Следующий код представляет собой рабочую реализацию 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()); } }
источник
Из Angular> = 2.3.0 вы можете переопределить
HTTP
модуль и внедрить свои службы. До версии 2.3.0 вы не могли использовать внедренные сервисы из-за основной ошибки.Я создал суть, чтобы показать, как это делается.
источник
app.module
кодом? Спасибо.HTTP
куда-нибудь еще импортируете ?Angular> 4.3: ErrorHandler для базовой службы
источник