2016-03-03 178 views
0

我目前正在編寫一個Angular 2 web應用程序,我想創建一個'攔截器'來檢測xhr請求是否因爲authorization (401)而被拒絕。AngularJS 2 Observable - xhr失敗重定向

如果發生這種情況,我希望我的應用程序重定向到登錄頁面。

我不知道如何繼續這樣的行爲,任何想法,最佳實踐將不勝感激。

回答

1

擴展<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

+0

非常感謝您分享這個例子。 我能夠調整它,以便以某種方式將其集成到我的應用程序中。 現在我遇到了一個錯誤,您可以在plunker上重現這一事實,即歷史記錄狀態被/ login登錄過多次。你知道爲什麼會發生這種情況嗎? – Linvi

+0

您可以通過以下步驟輕鬆進行復制: 在新頁面中打開活頁夾。 將頁面設置爲chrome選項卡(以查看歷史狀態)。 點擊登錄鏈接(你會看到歷史有2'/'狀態) 點擊受保護的鏈接(你會看到歷史記錄有'/ login') 點擊返回(沒有任何事情發生,我們是從/登錄到/登錄 - 這是問題)。 點擊返回(我們終於重定向到主頁)。 – Linvi

+0

我已經能夠確定問題來自router.navigate,這是在歷史記錄中添加兩次狀態。 – Linvi

1

你可以這樣做:

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服務器,儘管不安全並且不會在生產中推薦,它可以爲您節省一些時間在開發中!

0

的一種方法可能是延長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類中注入的當前路由器登錄頁面。

+0

感謝你爲這個。這一定會有所幫助。 我會嘗試在此注入我的身份驗證服務,並查看應用程序是否正在對其執行操作。我會及時向大家發佈。 – Linvi