使用Angular2,如何在登录重定向之前重定向到先前的URL?

21
使用Angular2创建单页应用程序时,我在自定义的RouterOutlet中拦截未经身份验证的用户访问非公共路由,并将其重定向到登录视图。成功登录后,我希望将用户重定向到他们最初请求的视图,而不是默认视图。
我注意到Router有一个renavigate()函数,可以导航到最后一个成功的路由,但是最后一个成功的路由是/auth/login而不是原始请求的URL。
基本上:如何访问或确定先前请求的URL?
除非必须,我真的不想通过传递查询字符串参数来解决问题。理想情况下,类似于backbone.history,我希望作为Router组件的一部分访问history集合!

location.back() 怎么样? - Günter Zöchbauer
这个答案可能会有用:https://dev59.com/vlkS5IYBdhLWcg3wVlTC#59008239 - AmirReza-Farahlagha
4个回答

25
  1. 使用身份验证守卫(实现CanActivate接口)以防止未经身份验证的用户。请参阅官方文档中的示例和此博客文章
  2. 在身份验证守卫中使用RouterStateSnapshot来捕获请求的URL。

canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) 
{
    // keep the attempted URL for redirecting
    this._loginService.redirectUrl = state.url;
}
  • 成功验证后使用 Router(例如在 login.component.ts 中)重定向到该URL。例如:this._router.navigateByUrl(redirectUrl);

  • P.S. @MichaelOryl 和 @Vitali 提出的建议也可行,但我的方法更符合 Angular2 的最终发布版本。


    1
    请注意,state.url 实际上并不包含用户输入的完整 URL - 它包含指向被保护状态的 URL。因此,(1) 此方法不会重定向到受保护状态的子级,(2) 任何辅助路由都将从 URL 中删除。 - Isaac
    @JonathanMiles,解释似乎很简单,但我恐怕不明白。我不知道如何创建“_loginService”。 - Aaron C
    1
    @AaronC,创建登录服务超出了问题的范围。只是为了指向正确的方向,请参阅官方服务教程和一些示例:来自Auth0来自Jason Watmore - Alex Klaus
    1
    (i) 首先感谢Alex Klaus提供的完美解决方案; (ii) 我猜Aaron C当时正在努力找出是否需要使用特定的库/服务。_loginService是指您设计/使用并注入到auth.guard.ts构造函数中的任何类型的登录或身份验证服务。作为一个新手和不太有才华的开发者,这就是我理解这个备注的方式。注意保重,祝好运和感谢。 - qqtf

    8
    您可以在Location类的文档中找到所需内容。使用back()函数可能会帮助您。
    另一种方法是订阅Location中的popstate事件。您可以查看MDN文档了解可能收到的值。
    class MyCLass {
      constructor(private location: Location) {
        location.subscribe((val) => {
            // do something with the state that's passed in
        })
      }
    }
    

    否则,您可能需要一项跟踪路由器更改的服务,以便您可以访问它们。
    class MyTrackingService {
      constructor(private router: Router) {
        router.subscribe((val) => {
            // store the routes here so that you can peel them off as needed?
        })
      }
    }
    

    在这种情况下,我正在访问Router对象并订阅任何更改,以便我可以追踪它们。

    感谢您的回复,这是一个可以考虑的选项。理想情况下,我正在寻找Angular2最佳实践,尽管我也知道它仍处于BETA版本并可能会更改。我注意到Location>PlatformLocation包含一个历史记录集合,但似乎没有有效的方法从路由器中与其交互;这是我首选的方法和Angular2推荐的应用程序导航方法。 - Jon Miles

    6
    这对我很有帮助。将其注入到主应用程序组件的构造函数中,并在引导方法中注册它即可。 页面加载时第一个 val 应该是原始URL。 我立即取消订阅以提高效率,因为我不想在此服务中监听后续路由器事件(也许还没有这个需求)。 将该服务注入到其他需要使用原始URL的地方。
    import { Injectable } from '@angular/core';
    import { Router } from '@angular/router';
    import { Subscription } from 'rxjs/Subscription';
    
    @Injectable()
    export class UrlTrackingService {
    
      public originalUrl: string;
    
      constructor(
        private router: Router
      ) {
        let subscription: Subscription = this.router.events.subscribe((val) => {
          this.originalUrl = val.url;
          subscription.unsubscribe();
        });
      }
    
    }
    

    在你的 bootstrap 方法中添加这个服务,这样你就可以在其他组件或服务中使用它。 bootstrap(AppComponent, [ ..., OriginalUrlService, ... ]).catch(err => console.error(err)); 然后像使用任何其他服务一样注入这个服务(例如 http),并使用 originalUrl 属性根据需要执行路由重定向。在你自己的组件或服务中添加: ` constructor( private ous: OriginalUrlService, private router: Router ) { ... } private someMethod(): void { this.router.navigateByUrl(this.ous.originalUrl); } ` - Vitali Kniazeu

    4

    使用Angular 2.2.1更新的示例

    身份验证守卫会将原始URL传递给登录组件:

    import { Injectable } from '@angular/core';
    import { Router, CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router';
    
    @Injectable()
    export class AuthGuard implements CanActivate {
    
        constructor(private router: Router) { }
    
        canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
            if (localStorage.getItem('currentUser')) {
                // logged in so return true
                return true;
            }
    
            // not logged in so redirect to login page with the return url
            this.router.navigate(['/login', { returnUrl: state.url }]);
            return false;
        }
    }
    
    import { Component, OnInit } from '@angular/core';
    import { Router, ActivatedRoute } from '@angular/router';
    
    import { AlertService, AuthenticationService } from '../_services/index';
    
    @Component({
        moduleId: module.id,
        templateUrl: 'login.component.html'
    })
    

    登录组件,在登录后会重定向到之前/原始的URL:

    export class LoginComponent implements OnInit {
        model: any = {};
        loading = false;
        returnUrl: string;
    
        constructor(
            private route: ActivatedRoute,
            private router: Router,
            private authenticationService: AuthenticationService,
            private alertService: AlertService) { }
    
        ngOnInit() {
            // reset login status
            this.authenticationService.logout();
    
            // get return url from route parameters or default to '/'
            this.returnUrl = this.route.snapshot.params['returnUrl'] || '/';
        }
    
        login() {
            this.loading = true;
            this.authenticationService.login(this.model.username, this.model.password)
                .subscribe(
                    data => {
                        // login successful so redirect to return url
                        this.router.navigate([this.returnUrl]);
                    },
                    error => {
                        // login failed so display error
                        this.alertService.error(error);
                        this.loading = false;
                    });
        }
    }
    

    您可以查看这篇文章以获取更多细节和工作演示。


    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接