AngularJS 2 Observable - xhr 重定向失败

AngularJS 2 Observable - xhr fail redirect

我目前正在编写一个 Angular 2 网络应用程序,我想创建一个 'interceptor' 来检测是否由于 authorization (401).[=12 而拒绝了 xhr 请求=]

如果发生这种情况,我希望将我的应用程序重定向到登录页面。

我不确定如何进行这种行为,任何想法,最佳实践将不胜感激。

你可以这样做:

export class CustomHTTPConnection implements Connection
{
}

请注意,Connection 是一个抽象 class,因此您必须完全从头开始构建它。

另一种方法是采用接近于同一事物的 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 class.

上注入的当前路由器将用户重定向到登录页面

扩展 <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 is an object that represents the route data for a given component, frankly, the information about the previous and next route to be invoked.

目前只有一个"problem",就是不能将依赖注入到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 文件中 Angular 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);
});

...你知道 class 将根注入器传递给它。您现在可以通过简单地导入它来从其他地方获得对您的注入器的引用

import {appInjector} from './app-injector';

let injector = appInjector();
let someDep = injector.get(...);

这里这种方法的好处是它更加可见和灵活。扩展 <router-outlet> 以某种方式隐藏在幕后。

无论如何,您可以在 Brandon 的 Plunker 中找到上述内容的实时代码示例:http://plnkr.co/edit/SF8gsYN1SvmUbkosHjqQ?p=preview