为Angular 6中的有效后卫引发的NavigationCancel事件

时间:2018-10-10 20:06:46

标签: angular angular-router angular-guards

在导航到提供状态的过程中,Angular路由器出现问题。

我尝试使用具有canLoad()canActivate()函数的自定义防护,将布尔值返回为true,但没有任何运气。

角度docs指出以下内容:

  

NavigationCancel::取消导航时触发的事件。这是由于路线   导航期间警惕返回false。

由于没有从路由器跟踪中获得更多的调试信息(没有给出任何原因),我不得不在此处检查是否有修复程序,或者这是否是现有的错误。我也感谢有关在Angular 6中调试路由器的其他方法的任何信息。

控制台输出

console output

我在here中创建了一个小项目。该项目需要访问提供者,我正在使用angular-oauth2-oidc信息库中提供的提供的OpenID Connect提供者。密码/用户名是max / geheim。

如何重现错误

  1. 克隆存储库并在localhost:4200上提供站点
  2. 转到localhost:4200 / oversikt / index
  3. 使用max / geheim作为用户名/密码登录
  4. 阅读控制台

更新 我怀疑这与导航到children: []条路线有关。

1 个答案:

答案 0 :(得分:7)

我对您的代码进行了一些调试,发现不是由于链接,身份验证保护,导航声明或模块配置,也不是因为AG6中的 router beeing flakey ,但这是因为由于.... 您正在使用的OAuth库

但是让我解释一下。我发现正在发生以下情况:

  1. 您登录并被重定向到页面
  2. 您使用/#YOUR AUT TOKEN之类的链接返回到应用程序
  3. 已安排第一次导航(id 1)
  4. 导航导致重定向到子页面/ oversikt
  5. 弹出导航2(重定向)
  6. 路由器以异步方式执行所有操作-它们已被序列化但仍使用RxJS-因此实际处理被延迟-它已将当前导航id传递为参数(这很重要)
  7. 一些监听器和其他订阅以及您正在使用的OAUTH库中的内容触发
  8. 它(lib)检测到您的URL中有令牌并将其删除
  9. 这会触发另一种重定向-路由ID即将增加到3
  10. 现在导航2继续进行,并且在某个时候(记住JS是单线程的),它会检查(导航ID会执行很多操作)从导航开始的导航ID是否更改还是仍然相同。
  11. 由于它已更改-id 2在计划时间传递,并且当前导航为3,因此单个false会传播到整个管道并在路由器中进行订阅
  12. 在管道中将boolean(甚至不是false,而是bool)作为值会导致CANCEL导航,没有任何原因。

将添加一些代码引用。但总的来说,这就是发生的事情。您的oauth lib会在导航期间修改url,这会导致其被取消。警卫们不希望直接采取任何行动。

因此,通常来说,它不会因为“进入被拒绝”而被取消,就像在警卫的情况下一样,但是由于必须执行新的导航而被取消,因此它会通过取消被短路。

以下是(不是全部)相关代码:

OAuth库修改

   if (!this.oidc) {
            this.eventsSubject.next(new OAuthSuccessEvent('token_received'));
            if (this.clearHashAfterLogin && !options.preventClearHashAfterLogin) {
                location.hash = '';
            }
            return Promise.resolve();
        }

在网址更改时触发导航:

 Router.prototype.setUpLocationChangeListener = function () {
        var _this = this;
        // Don't need to use Zone.wrap any more, because zone.js
        // already patch onPopState, so location change callback will
        // run into ngZone
        if (!this.locationSubscription) {
            this.locationSubscription = this.location.subscribe(function (change) {
                var rawUrlTree = _this.parseUrl(change['url']);
                var source = change['type'] === 'popstate' ? 'popstate' : 'hashchange';
                if(this.rou)
                var state = change.state && change.state.navigationId ?
                    { navigationId: change.state.navigationId } :
                    null;
                setTimeout(function () { console.error("FROM LOCATION SUB");_this.scheduleNavigation(rawUrlTree, source, state, { replaceUrl: true }); }, 0);
            });
        }
    };

导航ID修改-立即发生:

   var id = ++this.navigationId;
    console.error("ANOTHER SCHEDULED LOL LOL LOL!!!");
    this.navigations.next({ id: id, source: source, state: state, rawUrl: rawUrl, extras: extras, resolve: resolve, reject: reject, promise: promise });
    // Make sure that the error is propagated even though `processNavigations` catch
    // handler does not rethrow
    return promise.catch(function (e) { return Promise.reject(e); });

这就是传递给路由器以启动“异步”路由的内容-id是nav id(以前增加了)

 Router.prototype.runNavigate = function (url, rawUrl, skipLocationChange, replaceUrl, id, precreatedState) {

此检查(在runNav中进行)由于ID更改而第一次失败,因此2!== 3-FALSE返回到管道

var preactivationCheckGuards$ = preactivationSetup$.pipe(mergeMap(function (p) {
            if (typeof p === 'boolean' || _this.navigationId !== id) //NAVIGATION ID CHANGES HERE!
            {
              console.warn("PREACTIVATE GUARD CHECK ");
              console.log(p);
              // debugger;
              return of(false);
            }

在追赶中有几个订阅,它们都有一些管道映射等,以及已知的条件检查。

 var preactivationResolveData$ = preactivationCheckGuards$.pipe(mergeMap(function (p) {
                if (typeof p === 'boolean' || _this.navigationId !== id)
                    return of(false);

请注意,这是我之前写的内容,如果您在此处得到任何布尔值,则将false推入。由于我们这里有false,因为以前的管道图检查失败。...

最后在链的末端

    if (typeof p === 'boolean' || !p.shouldActivate || id !== _this.navigationId || !p.state) {
      // debugger;
        navigationIsSuccessful = false;
        return;
    }

结果标志设置为false,结果为

 .then(function () {
        if (navigationIsSuccessful) {
            _this.navigated = true;
            _this.lastSuccessfulId = id;
            _this.events
                .next(new NavigationEnd(id, _this.serializeUrl(url), _this.serializeUrl(_this.currentUrlTree)));
            resolvePromise(true);
        }
        else {
            _this.resetUrlToCurrentUrlTree();
            _this.events
                .next(new NavigationCancel(id, _this.serializeUrl(url), ''));
            resolvePromise(false);
        }

NavigationCancel,没有任何消息(最后一个参数是消息-这里是空字符串):)。

花了我很多钱,因为我不知道有角内部零件+那些血腥的管道...到处都是管道。

对于文档

  

NavigationCancel:取消导航时触发的事件。这个   是因为导航仪在导航期间返回了false。

好吧,他们忘记提及路由器的内部可以取消导航,因为导航队列正在累积:)

干杯!