Angular를 사용하여 401s를 전 세계적으로 처리
Angular 2 프로젝트에서 저는 관찰 가능한 것을 반환하는 서비스에서 API 호출을 합니다.그런 다음 호출 코드가 이 관찰 가능한 항목에 가입합니다.예:
getCampaigns(): Observable<Campaign[]> {
return this.http.get('/campaigns').map(res => res.json());
}
서버가 401을 반환한다고 가정해 보겠습니다.글로벌하게 이 오류를 감지하고 로그인 페이지/구성 요소로 리디렉션하려면 어떻게 해야 합니까?
감사해요.
지금까지 확인한 내용은 다음과 같습니다.
부트.츠
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]
})
]);
사용자 지정 http.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이 함수가 아닙니다"라는 오류 메시지가 표시됩니다.
앵귤러 4.3+
HttpClient의 도입으로 모든 요청/응답을 쉽게 가로채는 기능이 등장했습니다.HttpInterceptor의 일반적인 사용법은 잘 문서화되어 있습니다. 기본 사용법과 인터셉트기 제공 방법을 참조하십시오.다음은 401개의 오류를 처리할 수 있는 HttpInterceptor의 예입니다.
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);
}
})
);
}
}
RxJS < 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
}
});
}
}
묘사
내가 찾은 가장 좋은 해결책은 다음과 같은 문제를 해결하는 것입니다.XHRBackend
HTTP 응답 상태가 유지되도록 합니다.401
그리고.403
특정 동작으로 이어집니다.
Angular 응용 프로그램 외부에서 인증을 처리하는 경우 현재 페이지를 강제로 새로 고쳐 외부 메커니즘이 트리거될 수 있습니다.아래 구현에서 이 솔루션에 대해 자세히 설명합니다.
또한 Angular 응용프로그램이 다시 로드되지 않도록 응용프로그램 내부의 구성요소로 전달할 수 있습니다.
실행
각도 > 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 }
]
라우터와 외부 인증 서비스를 사용하는 또 다른 솔루션은 @mrgoos에 의해 다음 요지에 자세히 설명되어 있습니다.
각도 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 {
}
프론트엔드 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;
})
);
}
}
그Observable
각 요청 방법이 유형입니다.Observable<Response>
.그Response
, 소자가 .status
를 .401
서버가 해당 코드를 반환한 경우.따라서 매핑하거나 변환하기 전에 검색할 수 있습니다.
이의 "Angular 2"를 해야 할 .Http
를 지정하고합니다.super
the regular )에 대한 설명합니다.Http
을 수행한 ▁the▁function를 처리합니다.401
개체를 반환하기 전에 오류가 발생했습니다.
참조:
https://angular.io/docs/ts/latest/api/http/index/Response-class.html
앵귤러 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'));
});
}
}
라우터와 같은 서비스가 Http 파생 클래스에 주입되어 발생하는 순환 참조 문제를 방지하려면 사후 생성자 인젝터 방법을 사용해야 합니다.다음 코드는 REST API가 "Token_Expired"를 반환할 때마다 로그인 경로로 리디렉션하는 Http 서비스의 작동 구현입니다.일반 Http 대신 사용할 수 있으므로 응용 프로그램의 기존 구성 요소나 서비스를 변경할 필요가 없습니다.
app.s.ts.
providers: [
{provide: Http, useClass: ExtendedHttpService },
AuthService,
PartService,
AuthGuard
],
extended-message.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 이전에는 코어 버그로 인해 주입된 서비스를 사용할 수 없었습니다.
어떻게 하는지 보여줄 요지를 만들었습니다.
Angular >4.3: 기본 서비스에 대한 ErrorHandler
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);
}
언급URL : https://stackoverflow.com/questions/34934009/handling-401s-globally-with-angular
'sourcetip' 카테고리의 다른 글
Ubuntu Docker 이미지 내에서 wget을 실행하는 방법은 무엇입니까? (0) | 2023.09.05 |
---|---|
윈도우즈 PowerShell에서 Node.js 환경 변수 전달 (0) | 2023.09.05 |
내 sqdump는 crontab에서 작동하지 않습니다. (0) | 2023.09.05 |
AWS SES SMTP 사용 - 오류 554 메시지 거부:이메일 주소가 확인되지 않았습니다.다음 ID가 지역 체크인에 실패했습니다. (0) | 2023.09.05 |
항목 또는 하위 항목이 태그와 일치하는 경우 항목 선택 (0) | 2023.09.05 |