我目前正在編寫一個Angular 2 web應用程序,我想創建一個'攔截器'來檢測xhr請求是否因爲authorization (401)
而被拒絕。AngularJS 2 Observable - xhr失敗重定向
如果發生這種情況,我希望我的應用程序重定向到登錄頁面。
我不知道如何繼續這樣的行爲,任何想法,最佳實踐將不勝感激。
我目前正在編寫一個Angular 2 web應用程序,我想創建一個'攔截器'來檢測xhr請求是否因爲authorization (401)
而被拒絕。AngularJS 2 Observable - xhr失敗重定向
如果發生這種情況,我希望我的應用程序重定向到登錄頁面。
我不知道如何繼續這樣的行爲,任何想法,最佳實踐將不勝感激。
擴展<router-outlet>
是一種方法,但我發現Auth0示例存在一些缺陷(並且如果由於錯誤使用定製路由器插座使用自定義管道,還會導致Angular 2中斷)。
我發現了一個替代方法,由Brandon Roberts on a GitHub issue提出,基本上使用@CanActivate
裝飾器。這個想法是用@CanActivate
註釋路由組件,其中包含當前用戶是否有權訪問給定路由的邏輯。
import {Component, View} from 'angular2/core';
import {CanActivate} from 'angular2/router';
import {Page} from './Page';
import {isLoggedIn} from './is-logged-in';
@Component({
...
})
@CanActivate((next: ComponentInstruction, previous: ComponentInstruction) => {
return isLoggedIn(next, previous);
})
export class Protected {
}
正如你所看到的,@CanActivate
採取與以前ComponentInstruction
一個功能,而下一個你要轉型。
ComponentInstruction是表示給定組件的路由數據的對象,坦率地說,要被調用有關以前和下一個路由的信息。
目前只有一個「問題」,它是,你不能注入依賴到CanActivate函數。這就是爲什麼你必須應用一種解決方法,即將注射器(理想情況下是根注射器)存儲在某處。
// app-injector.ts
import {Injector} from 'angular2/core';
let appInjectorRef: Injector;
export const appInjector = (injector?: Injector):Injector => {
if (injector) {
appInjectorRef = injector;
}
return appInjectorRef;
};
然後,在你的main.ts
文件,其中的2角引導邏輯駐留......
// main.ts
import {bootstrap} from 'angular2/platform/browser';
...
import {App} from './app';
import {appInjector} from './app-injector';
bootstrap(App, [
...
]).then((appRef: ComponentRef) => {
// store a reference to the application injector
appInjector(appRef.injector);
});
...你這個類通過根注射器給它。現在,您可以通過導入它像
import {appInjector} from './app-injector';
let injector = appInjector();
let someDep = injector.get(...);
這種方法在這裏的很好的副作用是,它是更加明顯和靈活讓你注入從其他地方參考。擴展<router-outlet>
以某種方式隱藏在幕後。
不管怎麼說,你可以在布蘭登的Plunker發現上面的實時代碼示例:http://plnkr.co/edit/SF8gsYN1SvmUbkosHjqQ?p=preview
你可以這樣做:
export class CustomHTTPConnection implements Connection
{
}
請注意,連接是一個抽象類,所以你就必須從頭開始完全建立它。
另一種方法是採用Auth0方式,這種方式接近於同一事物。 https://github.com/auth0/angular2-jwt/blob/master/angular2-jwt.ts,然後你可以在那裏添加處理401錯誤,並將它們發送到401上的登錄頁面。
或者,如果用戶未登錄,則可以對所有路由設置限制,並始終重定向登錄如果他們嘗試訪問您網站的其他部分。
建立一個新的路由器插座,它像另一個那樣工作,但通過激活(指令:ComponentInstruction)檢查它們是否已登錄。你也可以添加publicRoutes來說你可以不登錄就進入某些頁面。**警告你不能這樣做,這種方式使用HashLocationStrategy,因爲它不會正確記錄this.parentRouter.lastNavigationAttempt。
import {Directive, Attribute, ElementRef, DynamicComponentLoader} from 'angular2/core';
import {Router, RouterOutlet, ComponentInstruction} from 'angular2/router';
import {Login} from '../login/login.component';
@Directive({
selector: 'router-outlet'
})
export class LoggedInRouterOutlet extends RouterOutlet {
publicRoutes: any;
private parentRouter: Router;
constructor(_elementRef: ElementRef, _loader: DynamicComponentLoader,
_parentRouter: Router, @Attribute('name') nameAttr: string) {
super(_elementRef, _loader, _parentRouter, nameAttr);
this.parentRouter = _parentRouter;
this.publicRoutes = {
};
}
activate(instruction: ComponentInstruction) {
var url = this.parentRouter.lastNavigationAttempt;
console.log(url);
if (!this.publicRoutes[url] && !localStorage.getItem('jwt')) {//Public Routes does not work with Hash Location Strategy, need to come up with something else.
// todo: redirect to Login, may be there is a better way?
this.parentRouter.navigate(['Login']);
}
return super.activate(instruction);
}
}
然後,只需調用它就像你通常會:
import {LoggedInRouterOutlet} from './utility/LoggedInOutlets';
@Component({
selector: 'my-app',
directives: [LoggedInRouterOutlet],
template: `<div class="container">
<router-outlet></router-outlet>
</div>`
})
現在,這會檢查自己是否在每次他們去另一條路線的時間記錄下來,這樣做它很酷的事情這種方式是我不必每次都ping服務器,儘管不安全並且不會在生產中推薦,它可以爲您節省一些時間在開發中!
的一種方法可能是延長HTTP
對象攔截錯誤:
@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).catch(res => {
// do something
});
}
get(url: string, options?: RequestOptionsArgs): Observable<Response> {
console.log('get...');
return super.get(url, options).catch(res => {
// do something
});
}
}
並將其註冊,如下所述:發生
bootstrap(AppComponent, [HTTP_PROVIDERS,
new Provider(Http, {
useFactory: (backend: XHRBackend, defaultOptions: RequestOptions) => new CustomHttp(backend, defaultOptions),
deps: [XHRBackend, RequestOptions]
})
]);
如果401錯誤作爲這個水平,可以重定向用戶根據您在此CustomHttp
類中注入的當前路由器登錄頁面。
感謝你爲這個。這一定會有所幫助。 我會嘗試在此注入我的身份驗證服務,並查看應用程序是否正在對其執行操作。我會及時向大家發佈。 – Linvi
非常感謝您分享這個例子。 我能夠調整它,以便以某種方式將其集成到我的應用程序中。 現在我遇到了一個錯誤,您可以在plunker上重現這一事實,即歷史記錄狀態被/ login登錄過多次。你知道爲什麼會發生這種情況嗎? – Linvi
您可以通過以下步驟輕鬆進行復制: 在新頁面中打開活頁夾。 將頁面設置爲chrome選項卡(以查看歷史狀態)。 點擊登錄鏈接(你會看到歷史有2'/'狀態) 點擊受保護的鏈接(你會看到歷史記錄有'/ login') 點擊返回(沒有任何事情發生,我們是從/登錄到/登錄 - 這是問題)。 點擊返回(我們終於重定向到主頁)。 – Linvi
我已經能夠確定問題來自router.navigate,這是在歷史記錄中添加兩次狀態。 – Linvi