#angular #angular2-routing
#angular #angular2-маршрутизация
Вопрос:
У меня есть URL-адреса как http://www.localhost:4200/profile
и http://www.localhost:4200/editProfile
. Оба URL-адреса передаются зарегистрированному пользователю. Теперь я хочу, чтобы доступ к /editProfile
был доступен только через меню навигации, а не путем прямого ввода URL-адреса в адресной строке и нажатия enter. Если пользователь делает это, он перенаправляется на /profile
path.
Что-то похожее на разрешение POST на /editProfile
, но без GET.
Может ли это быть достигнуто с помощью canActivate, доступного в модуле routes?
Спасибо
Комментарии:
1. Почему это нормально, если пользователь переходит к этому маршруту через меню навигации, но не напрямую через адресную строку браузера?
2. бизнес-требования.
3. Во-вторых, помимо этого, существует множество подобных сценариев, где это может быть полезно. Например, если есть страница сводки корзины покупок, то вы не хотите, чтобы пользователь посещал ее напрямую, не переходя на страницы оформления заказа. верно?
4. Я бы не стал блокировать прямую навигацию в своем приложении.
AuthGuard
, который проверяет, авторизован ли пользователь, должно быть достаточно. Тем не менее, я написал ответ для вас.
Ответ №1:
Итог. Я спросил вас с этой целью, потому что способ, которым я решаю эту проблему, заключается не в том, чтобы запретить пользователю перемещаться по адресной строке. Это нарушит работу пользователей, которые должны иметь законный доступ к этой странице. Если пользователь уже вошел в систему, почему ему не должен быть разрешен прямой доступ к edit profile
странице? Это также приведет к сбоям, когда пользователь попытается использовать кнопки навигации вперед и назад в своем браузере, и, я думаю, это приведет к очень неприятному опыту.
Если вы все еще хотите это сделать…
Вы можете использовать CanActivate
в своем определении маршрута
path: 'editProfile',
component: EditProfileComponent,
canActivate:[EditProfileGuard]
EditProfileGuard
это сервис, который разрешает навигацию, только если установлен флаг true
@Injectable()
export class EditProfileGuard implements CanActivate {
//must be set to true for navigation to succeed
allow = false;
canActivate(){
if(this.allow){
this.allow = false;
return true;
}
else return false;
}
}
Если пользователь переходит через адресную строку браузера, ему будет отказано в доступе, потому что allow
значение false.
Когда пользователь нажимает на ссылку в вашем навигационном меню, установите allow
значение true, прежде чем отправлять его на новый маршрут
import {EditProfileGuard} from '...'
import {Router} from '@angular/router';
...
export class MyComponent{
constructor(private guard:EditProfileGuard, private router:Router){}
//execute this when link is clicked
goToProfile(){
//so navigation will be allowed
this.guard.allow = true;
this.router.navigateByUrl('editProfile');
}
}
Не забудьте добавить службу в providers
массив вашего AppModule
.
В ответ на ваши комментарии ниже:
Я бы сделал checkoutID
обязательным параметром для страницы сводки, чтобы определением маршрута была /summary/:id
ссылка на страницу оформления заказа (или URL, сохраненный пользователем), которая будет иметь id
, и компонент сводки может использовать это id
для извлечения и отображения сведений.
Если пользователь, который не выполнил проверку, попытается перейти непосредственно на страницу сводки, id
будет отсутствовать, и навигация завершится ошибкой.
В ngOnInit
для компонента summary я бы проверил id
, чтобы, если пользователь изобретет подделку id
и попытается перейти, я мог перенаправить вместо загрузки компонента.
Это позволит законным пользователям перемещаться напрямую, а навигация вперед / назад будет работать.
Комментарии:
1. Спасибо за ответ @BeetleJuice, но как насчет второго сценария, о котором я упоминал, согласны ли вы поддерживать каждый такой маршрут с использованием gaurd? Второй сценарий: если в корзине покупок есть страница со сводкой, то вы не хотите, чтобы пользователь посещал ее напрямую, не переходя на страницы оформления заказа.
2. Я бы посоветовал вам ознакомиться с этой страницей в качестве справочной flipkart.com/viewcart . Добавьте товар в корзину и перейдите к покупке, теперь, не заходя в корзину, попробуйте перейти по этой ссылке flipkart.com/checkout/init . Это не позволяет вам того же. Надеюсь, это объясняет лучше
3. @SumitAgarwal Я добавил абзац к своему ответу. Если вы решите использовать мои идеи, не забудьте выбрать ответ и проголосовать за него. Удачи.
4. но что, если пользователь отредактирует сгенерированный javascript и жестко закодирует переменную «разрешить», чтобы она всегда была «true»?
5. @arvstracthoughs В конечном счете, пользователь может взять под контроль все, что Javascript делает на странице. Если пользователь столкнется с трудностями при выполнении этого, он может изменить все, что делает ваше приложение в браузере. Если вы пытаетесь сохранить ценную информацию от пользователя, это должно быть сделано на стороне сервера; никогда не отправляйте эту информацию в браузер. Блок JavaScript предназначен только для удобства, поэтому пользователь не видит пустую страницу, где будет профиль или сводка корзины.
Ответ №2:
Отличный ответ @BeetleJuice. На самом деле вы можете воспользоваться тем фактом, что внешнее событие маршрута повторно создаст ваше приложение, тем самым очистив все переменные (ошибки в маршрутизации Angular).
Сделав еще один шаг, вы можете создать вторую службу для абстрагирования «внутренней» маршрутизации:
internal-router.service.ts
import { Injectable } from '@angular/core';
import { Router, NavigationExtras, UrlTree } from '@angular/router';
@Injectable()
export class InternalRouter {
private _isNavigationAllowed = false;
get isNavigationAllowed(): boolean {
return this._isNavigationAllowed;
}
constructor(private router: Router) { }
navigate(commands: any[], extras?: NavigationExtras): Promise<boolean> {
return this.runNavigation(() => this.router.navigate(commands, extras));
}
navigateByUrl(url: string | UrlTree): Promise<boolean> {
return this.runNavigation(() => this.router.navigateByUrl(url));
}
private runNavigation(navigation: () => Promise<boolean>): Promise<boolean> {
this._isNavigationAllowed = true;
return navigation()
.then(success => {
this._isNavigationAllowed = false;
return Promise.resolve(success);
})
.catch(e => {
this._isNavigationAllowed = false;
return Promise.reject(e);
});
}
}
только для внутренних.guard.ts
import { Injectable } from '@angular/core';
import { CanActivate, Router } from '@angular/router';
import { InternalRouter } from './internal-router.service';
@Injectable()
export class InternalOnlyGuard implements CanActivate {
constructor(private router: Router, private internalRouter: InternalRouter) { }
canActivate(): boolean {
if (!this.internalRouter.isNavigationAllowed) {
this.router.navigateByUrl('/'); // Or other logic
return false;
}
return true;
}
}
На что следует обратить внимание:
- Защита использует реальный маршрутизатор при перенаправлении, чтобы избежать бесконечного цикла в некоторых ситуациях (в зависимости от ваших маршрутов, перенаправлений и других средств защиты)
runNavigation()
в службе маршрутизатора выполняется холодное обещание, поэтому он не запускает маршрутизацию, пока не будет готов.
Я нашел этот подход приятным, потому что InternalRouter
он несет полную ответственность за свое собственное состояние.