16

我正在使用 Angular 2.1.2。

我有一个身份验证令牌(使用 angular2-jwt),如果它过期,我的 webApi 调用将失败并出现 401 错误。我正在寻找用户不会丢失任何输入数据的解决方案。

我可以捕捉到这个 401 并使用登录打开一个模式。然后用户登录,模式消失,他们看到他们的输入屏幕。但是,失败的请求显示错误,因此我需要重新处理请求。如果是路由器导航,则初始数据尚未加载。

我可以重新加载页面,但如果我 router.navigate 到同一页面,它似乎并没有真正重新加载页面。我不想在单页应用程序上重新加载整页。有没有办法强制 router.navigate 运行,即使它是当前页面?

重新导航仍然是一个问题,因为我会丢失任何尚未保存的新输入数据。

理想情况下,请求只会“暂停”,直到用户从模式登录。我还没有找到实现这一点的方法。

有任何想法吗?有最佳实践吗?

4

5 回答 5

5

通常我会提供一个HttpService自己而不是Http直接使用。get()因此,根据您的要求,我可以在发送任何真正的 HTTP 请求之前提供自己的方法来链接身份验证。

这是服务:

@Injectable()
class HttpService {
  constructor(private http: Http, private auth: Authentication) {}

  public get(url: string): Observable<Response> {
    return this.auth.authenticate().flatMap(authenticated => {
      if (authenticated) {
        return this.http.get(url);
      }
      else {
        return Observable.throw('Unable to re-authenticate');
      }
    });
  }
}

这是调用服务的组件:

@Component({
  selector: 'my-app',
  template: `<h1>Hello {{name}}</h1>
  <button (click)="doSomething()">Do Something</button>

  <div [hidden]="!auth.showModal">
  <p>Do you confirm to log in?</p>
  <button (click)="yes()">Yes</button><button (click)="no()">No</button>
  </div>
  `,
})
export class AppComponent {
   name = 'Angular';

   constructor(private httpSvc: HttpService, public auth: Authentication) {}

   ngOnInit() {
   }

   doSomething() {
     let a = this.httpSvc.get('hello.json').subscribe(() => {
       alert('Data retrieved!');
     }, err => {
       alert(err);
     });
   }

   yes() {
    this.auth.confirm.emit(true);
   }

   no() {
     this.auth.confirm.emit(false);
   }
}

通过链接可观察对象,Authentication服务确定是否中断正常流程以显示模态(虽然目前仅与 App 组件一起存在,但它当然可以单独实现)。一旦从对话中收到肯定的回答,服务就可以恢复流程。

class Authentication {
  public needsAuthentication = true;
  public showModal = false;
  public confirm = new EventEmitter<boolean>();

  public authenticate(): Observable<boolean> {
    // do something to make sure authentication token works correctly
    if (this.needsAuthentication) {
      this.showModal = true;
      return Observable.create(observer => {
        this.confirm.subscribe(r => {
          this.showModal = false;
          this.needsAuthentication = !r; 
          observer.next(r);
          observer.complete();
        });
      });
    }
    else {
      return Observable.of(true);
    }
  }
}

我在这里有一个完整的例子。

http://plnkr.co/edit/C129guNJvri5hbGZGsHp?open=app%2Fapp.component.ts&p=preview

于 2016-11-27T16:19:54.297 回答
3

理想情况下,请求只会“暂停”,直到用户从模式登录。我还没有找到实现这一点的方法。

您是否尝试使用tokenNotExpired此示例中的属性切换按钮:https ://github.com/auth0/angular2-jwt#checking-authentication-to-hideshow-elements-and-handle-routing

它允许您防止 401...

于 2016-11-25T14:10:52.223 回答
3

在我登录并收到一个令牌(在我的情况下,它在 60 分钟内到期)后,我设置了一个间隔,每分钟检查一次,看看是否已经过了 59 分钟。如果是,那么我打开一个登录对话框。

然而,这个想法是登录对话框不是路由,而只是在用户碰巧在的任何屏幕上打开的叠加层(因此我将登录组件放在应用程序根组件的 html 中,并使用从应用程序的任何位置调用登录对话框的可观察对象)。

当用户正确重新登录时,我关闭登录对话框,用户在重新登录之前使用他们正在使用的任何屏幕愉快地前进。

我不知道这是否是“最佳实践”,但它适用于您所描述的情况。让我知道,我可以提供代码。

于 2016-11-25T22:21:44.370 回答
2

好吧,重新加载很简单:(<any>window).location.reload(true);

如果用户可以提供密码,最好显示登录/密码弹出窗口并允许用户继续工作,如果用户点击取消,只需重定向到登录页面。

还有连接问题、超时、服务器错误。问题是使用 Angular 2Http模块和rxjs. 就个人而言,我放弃了使用Http模块,因为它的错误处理设计糟糕,并实现了我自己的 http 模块,它允许可配置的错误处理和透明的重试。

于 2016-11-22T23:35:40.430 回答
2

使用浏览器会话:

https://developer.mozilla.org/de/docs/Web/API/Window/sessionStorage

将数据存储为 json 字符串,然后在请求失败时重新创建表单数据。

于 2016-11-22T12:31:35.223 回答