Как обрабатывать хеш-фрагменты из URL-адресов перенаправления oauth в маршрутизации Angular2 RC3

Я пытаюсь найти способ справиться с настройкой маршрута Angular2 Typescript (используя маршрутизатор 3.0.0-alpha.8), который будет обрабатывать маршруты, начинающиеся с хеш-фрагментов.

Приложение, над которым я работаю, обрабатывает все входы в систему извне (то, что я не могу контролировать) через бэкэнд рельсов с oauth2. Перенаправление пользователей на страницу внешнего входа работает нормально, но когда 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, Я также искал через stackru и не смог найти ничего, что работало бы с 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-адреса перенаправления в реальной жизни, и я не могу найти решение, которое может справиться с тем фактом, что оно начинается с фрагмента хеша, а не с / а затем мои параметры запроса. Все примеры маршрутизации со сложными символами или символами, которые я могу найти, начинаются с /,

Я попытался изменить свое решение выше, чтобы :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-адрес перенаправления, как это, но независимо от того, сколько я копаю в документах, я не могу найти его. Любой совет о том, как реализовать это, будет высоко ценится.

2 ответа

Решение

В конце концов мне удалось найти решение, которое использует предпочтительный PathLocationStrategy, но также извлекает токен из URI перенаправления oauth до того, как часть URL-адреса после отбрасывания фрагмента хеша (из окончательного ответа, полученного из QueryParams и Fragment). раздел в следующем сообщении в блоге).

По сути, я обновил URL перенаправления при регистрации моего приложения с doorkeeper/oauth2, чтобы он был http://localhost:4200/login/ (что приводит к тому, что URL перенаправления, содержащий токен, выглядит следующим образом http://localhost:4200/login/#access_token=TOKEN) и добавил следующий маршрут:

{
  path: 'login',
  component: LoginComponent
}

Это перехватывает URL перенаправления, но удаляет все после фрагмента хеша, удаляя нужный мне токен. Чтобы предотвратить сброс всего после фрагмента хеша, я добавил следующий код в конструктор моего 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 и в качестве значения trueNullChecks в tsconfig.json установлено значение 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);
    }

Не предполагая только индекс требуемого значения, вы можете использовать следующий подход, чтобы получить пару ключ-значение из нужного фрагмента.

`

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

`

в объекте "результат" вы можете получить доступ к требуемому значению по результату ['ключ']

Другие вопросы по тегам