问题
I have URLs as http://www.localhost:4200/profile
and http://www.localhost:4200/editProfile
. Both URLs are served to a logged in user. Now I want the accessibility of /editProfile
only through the navigation menu available and not by directly writing the URL on address bar and pressing enter. If the user does so, he is redirected to /profile
path.
Something similar to allowing POST on /editProfile
but no GET.
Can it be achieved using CanActivate available at the routes module?
Thanks
回答1:
Sumit. I asked you for the purpose because the way I solve that problem is not by preventing user from navigating via the address bar. Doing so will break things for users who should have legitimate access to that page. If a user is logged in already, why should she not be allowed to directly access edit profile
page? It will also break things when the user tries to use forward and back navigation buttons in her browser and will make for a very frustrating experience I think.
If you still want to do it...
You can use CanActivate
in your route definition
path: 'editProfile',
component: EditProfileComponent,
canActivate:[EditProfileGuard]
EditProfileGuard
is a service that will allow navigation only if a flag is set to 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;
}
}
If the user navigates via browser address bar, she will be denied access because allow
is false.
When user clicks a link in your nav menu, set allow
to true before you send her to the new route
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');
}
}
Remember to add the service to the providers
array of your AppModule
.
In response to your comments below:
I would make a checkoutID
a required parameter to the summary page, so the route definition would be /summary/:id
The link to the checkout page (or a url the user saved) would have the id
, and the summary component can use that id
to retrieve and display details.
If a user who has not checked out tries to navigate directly to the summary page, the id
will be missing and the navigation will fail.
In ngOnInit
for the summary component, I would validate the id
, so that if a user invents a fake id
and tries to navigate, I can redirect instead of loading the component.
This will allow legitimate users to navigate directly, and forward/back navigation to work.
回答2:
Great answer @BeetleJuice. You can in fact take advantage of the fact that an external route event will reinstantiate your app, thus clearing all variables (Angular Routing Gotchas).
Going a step further, you can create a second service to abstract out the "internal" routing:
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);
});
}
}
internal-only.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;
}
}
Things to note:
- The guard uses the real router when redirecting to avoid an infinite loop in some situations (depending on your routes, redirects, and other guards)
runNavigation()
in the router service takes a cold promise, so it doesn't start routing until it's ready.
I found this approach nice because InternalRouter
is solely responsible for its own state.
来源:https://stackoverflow.com/questions/40145779/angular-2-block-get-request-on-a-url