14

我正在尝试找到一种方法来处理设置将处理以散列片段开头的路由的 Angular2 Typescript 路由(使用 3.0.0-alpha.8 路由器)。

我正在开发的应用程序通过带有 oauth2 的 rails 后端处理所有外部登录(我无法控制)。将用户重定向到外部登录页面工作正常,但是当重定向 url 时,总是某种形式的http://localhost:4200#access_token=TOKEN(其中 TOKEN 是一系列数字和字母)被发回,但我不知道如何设置可以处理的路由#签名,以便我可以抓住它并重定向到适当的组件。

在以前的 Angular1 应用程序中ui-router,能够在以下路线中使用:

.state('accessToken', {
  url: '/access_token=:token',
  controller: 'LoginController',
  params: { token: null }
})

并且这可以毫无问题地接受发回的重定向 url,然后将所有内容传递给 LoginController 以在前端处理其余的身份验证/令牌业务。

然而,这个应用程序是 Angular2 和 Typescript,路由器查询参数似乎不太灵活,我在实施类似的解决方案时遇到了麻烦。我一直在基于文档中的这一部分进行操作,但是所有示例都在构建其他内容,例如/heroes在进入查询参数的复杂部分之前,例如/heroes/:id。我也搜索了 stackoverflow,但找不到任何适用于 Angular2 和 Typescript 以及当前路由器的东西。

这是我当前的(非工作)解决方案:

import { provideRouter, RouterConfig } from '@angular/router';

import { HomeComponent } from './components/home/home.component';
import { TestComponent } from './components/test/test.component';


export const appRoutes: RouterConfig = [
  {
    path: '',
    component: HomeComponent,
    terminal: true
  },
  {
    path: 'access_token',
    component: TestComponent
  }
];

export const APP_ROUTER_PROVIDERS = [
  provideRouter(appRoutes)
];

如果我将发回的重定向 url 修改为类似的东西(纯粹是为了测试目的),http://localhost:4200/access_token=TOKEN它就可以正常工作。不幸的是,在现实生活中,我实际上无法控制重定向 url 的格式,而且我无法想出一个解决方案来处理它以哈希片段而不是 a 开头,/然后是我的查询参数的事实。我能找到的所有带有复杂符号或字符的路由示例都以/.

我尝试将上面的解决方案修改为:access_token,但这不起作用,并将其列为基本路由下的子路由,如下所示:

{
  path: '',
  component: HomeComponent,
  terminal: true,
  children: [
    { path: 'access_token',  component: TestComponent },
  ]
}

这导致以下控制台错误: platform-browser.umd.js:2312 EXCEPTION: Error: Uncaught (in promise): Error: Cannot match any routes: ''

我觉得绝对必须有一个干净的解决方案来解决这个问题,特别是因为这么多 API 通过这样的重定向 url 处理他们的身份验证,但无论我如何挖掘文档,我似乎都找不到它。任何关于如何实现这一点的建议将不胜感激。

4

2 回答 2

16

我最终能够找到一个使用首选 PathLocationStrategy 的解决方案,但也可以在删除哈希片段之后的部分 url 之前将令牌从 oauth 重定向 uri 中提取出来(来自此处的最终答案,该答案是从 QueryParams 和 Fragment 中提取的以下博客文章中的部分)。

本质上,我在使用 doorkeeper/oauth2 注册我的应用程序时将重定向 url 更新为http://localhost:4200/login/(这导致包含令牌的重定向 url 看起来像http://localhost:4200/login/#access_token=TOKEN)并添加了以下路由:

{
  path: 'login',
  component: LoginComponent
}

这会捕获重定向 url,但会删除哈希片段之后的所有内容,从而删除我需要的令牌。为了防止它在哈希片段之后删除所有内容,我将以下代码添加到 my 的构造函数中LoginComponent

constructor(private activatedRoute: ActivatedRoute, 
            private router: Router, 
            private tokenService: TokenService) {

// Pulls token from url before the hash fragment is removed

const routeFragment: Observable<string> = activatedRoute.fragment;
routeFragment.subscribe(fragment => {
  let token: string = fragment.match(/^(.*?)&/)[1].replace('access_token=', '');
  this.tokenService.setToken(token);
});

}

您选择如何处理令牌取决于您(我有一个 TokenService,其中包含从 localStorage 设置、检索和清除它的方法),但这是您在哈希片段之后访问 url 部分的方式。如果有人有更好的解决方案,请随时在此处更新/发布。

更新:对上述登录组件代码的小更新,以处理 Angular v4.2.0 中的“片段可能为空”打字稿错误,并且在 tsconfig.json 中将 strictNullChecks 设置为 true,以防万一有人需要它。功能是一样的:

let routeFragment = this.activatedRoute.fragment.map(fragment => fragment);

routeFragment.subscribe(fragment => {
  let f = fragment.match(/^(.*?)&/);
  if(f) {
   let token: string = f[1].replace('access_token=', '');
   this.tokenService.setToken(token);
}

注意:从 RxJS 6 开始,map操作符已经变成了可管道化的,这意味着你必须通过如下所示的pipe方法来传递它:Observable

import { map } from 'rxjs/operators';

// ...

this.activatedRoute.fragment
  .pipe(map(fragment => fragment))
  .subscribe(fragment => {
    let f = fragment.match(/^(.*?)&/);
    if(f) {
      let token: string = f[1].replace('access_token=', '');
      this.tokenService.setToken(token);
    }
于 2016-07-27T18:18:10.887 回答
0

无需假设您需要的值的索引,您可以使用以下方法从您想要的片段中获取键值对。

`

constructor(private route: ActivatedRoute) {}

ngOnInit(): void {
    const fragment: string = this.route.snapshot.fragment;
    this.processFragment(fragment);
}

processFragment(fragmentString) {
    var ar = fragmentString.split('&');
    var result = {}
    for (var i = 0; i < ar.length; i++) {
        var split = ar[i].indexOf("=");
        var key = ar[i].slice(0, split);
        result[key] = ar[i].slice(split + 1);
    }
return result;
}

`

在“结果”对象中,您可以通过 result['key'] 访问所需的值

于 2021-10-01T05:03:50.150 回答