Angular 2: Block GET request on a URL

♀尐吖头ヾ 提交于 2019-12-24 09:01:32

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!