7

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

我曾尝试使用带有返回布尔值的自定义警卫canLoad()和函数,但没有任何运气。canActivate()true

Angular文档声明如下:

NavigationCancel:取消导航时触发的事件。这是由于 Route Guard 在导航期间返回 false。

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

控制台输出

控制台输出

我在这里创建了一个小项目。该项目需要访问提供程序,我正在使用 angular-oauth2-oidc 存储库中提供的 OpenID Connect 提供程序。密码/用户名是 max/geheim。

如何重现错误

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

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

4

1 回答 1

16

我在您的代码中做了一些调试,我发现这不是因为链接、身份验证保护、导航声明或模块配置,也不是因为AG6 中的路由器蜂鸣,而是因为..... OAuth lib 你正在使用.

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

  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,singlefalse传播到整个管道和路由器中的订阅
  12. boolean(甚至不是假的,而是布尔值)作为管道中的值会导致无缘无故地取消导航。

生病添加一些代码参考。但总的来说,这就是正在发生的事情。您的 oauth 库在导航期间修改了 url 并导致它被取消。卫兵直接与此无关。

所以一般来说 - 它不会像警卫一样因为“访问被拒绝”而被取消,而是因为必须执行新的导航而被取消,所以它被取消短路。

这是(不是全部)相关代码:

OAuth 库修改

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

url 更改时触发导航:

 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 是导航 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);
            }

chaing 中还有更多订阅,它们都有一些管道映射等以及已知的条件检查。

 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;
    }

结果标志设置为假,这会导致

 .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:取消导航时触发的事件。这是由于 Route Guard 在导航期间返回 false。

好吧,他们忘了提到内部路由器可以取消导航是否有导航队列建立:)

干杯!

于 2018-10-20T01:44:44.243 回答