【问题标题】:What is the best way to do routing of angular components using node.js as backend使用 node.js 作为后端路由角度组件的最佳方法是什么
【发布时间】:2019-03-31 06:44:59
【问题描述】:

我正在尝试浏览 Angular 组件(注册和仪表板)。如果用户已经注册,那么他应该被重定向到仪表板组件,如果没有,那么他应该被重定向到注册组件。仅运行 lib.js 文件作为服务器。使用ng build 命令(使用dist 文件夹)后部署角度文件。前端和后端都在同一个文件夹中

这里是一些代码sn-ps:

lib.js(node.js 后端文件)

app.use(exp.static(path.join(__dirname, 'dist')));

app.listen(PORT, function() {
    console.log('Express server listening on port ', PORT); // eslint-disable-line
});

app.post('/check_account',function(req,res){
    console.log(req.body);
    connection.query('SELECT * FROM login_table WHERE m_pub_key LIKE '+'\"'+req.body.m_pub_key+'\"' ,function(error,rows,fields){
        if(!!error){
            console.log('error!');
            res.send('Error!');
        }
        else
            {
                console.log(rows);
                if(rows.length!=0){
                    data={
                        is_signed_up: 1
                    }
                    res.send(data);
                }
                else{
                    data={
                        is_signed_up: 0
                    }
                    res.send(data);
                }
            }
    });

});

app.get('/dashboard',function(req,res){
    console.log("inside dashboard backend");
    res.sendFile(path.join(__dirname, 'dist/index.html'));
});

app.get('/signup', function (req,res) {
    console.log("signup backend");
    res.sendFile(path.join(__dirname, 'dist/index.html'));
});

app.get('/', function (req,res) {
    console.log("slash backend");
    res.sendFile(path.join(__dirname, 'dist/index.html'));
});

app.component.ts

  constructor(private router: Router,private _appService: AppService, private zone: NgZone, private http: HttpClient, private route: ActivatedRoute){
    console.log("inside app component constructor");
    this.checkAndInstantiateWeb3();
    this.onReady();

  }

  checkAccount(){
    let data_send = JSON.stringify({
            'm_pub_key': this.m_pub_key
        });

      this.zone.runOutsideAngular(()=>{
        this._appService.post_method(this.url+"check_account", data_send).subscribe(
          data => {
              this.is_signed_up=data["is_signed_up"];
              console.log(this.is_signed_up+"***************8");
              if(this.is_signed_up==1){
                console.log("navigating to dash");
                this.router.navigate(['/dashboard']);
                //this.http.get("/dashboard");
              }
              else{
                console.log("navigating to signuo");
                this.router.navigate(['/signup']);
                //this.http.get("/signup");
              }
          },
          error => {
              // console.log('post error');
          });
});
  }

  public checkAndInstantiateWeb3 = () => {
        if (typeof window.web3 !== 'undefined') {
            console.warn('Using injected web3');
            this.web3 = new Web3(window.web3.currentProvider);
        } else {
            // when running in browser, use incognito mode or log out of metamask
            console.warn('No web 3 detected, falling back to Ganache');
            this.provider = new Web3.providers.HttpProvider('http://localhost:7545');
            this.web3 = new Web3(this.provider);
        }
        window.ethereum.enable();
      }

  public onReady = () => {
        // get initial account balance so it can be displayed
        this.web3.eth.getAccounts((err, accs) => {
          if (err != null) {
            console.log(err);
            alert('There was an error fetching your accounts.');
            return;
          }

          if (accs.length === 0) {
            alert('You are not connected to an Ethereum client.');
            return;
          }
          this.m_pub_key=accs[0];
          console.log(this.m_pub_key);
          this.checkAccount();
        });
      }
}

app-routing.module.ts

const routes: Routes = [
    { path: 'signup', component: SignupComponent },
    { path: '', pathMatch: 'full', redirectTo: 'signup' },
    { path: 'dashboard', component: DashboardComponent },
];

@NgModule({
    imports: [RouterModule.forRoot(routes)],
    exports: [RouterModule],
})
export class AppRoutingModule { }

现在的问题是,如果用户已经注册,然后当他尝试转到任何页面时,他会被重定向到第一个注册页面,然后是仪表板页面,但是两个页面重叠并且仪表板组件也不起作用适当地。当用户使用 url 直接访问仪表板组件时,仪表板组件将按预期工作。对此问题的任何帮助将不胜感激。

编辑 1

按照“k0hamed”给出的答案。 像这样使用 canActivate 属性{ path: 'signup', component: SignupComponent, canActivate:[AuthGuard] } 这是我创建的保护和服务文件,它没有按预期工作。 auth.guard.ts 文件中的val 没有更改,当我转到 localhost:8080/ 或 localhost:8080/signup 时没有加载任何内容,但是当我输入 localhost:8080/dashboard 时会加载仪表板组件

auth.guard.ts:

export class AuthGuard implements CanActivate {
  canActivate(
    next: ActivatedRouteSnapshot,
    state: RouterStateSnapshot
    ): Observable<boolean> {
    console.log('AuthGuard#canActivate called');
    return this.signupService.onReady()
             .pipe(tap(val=>!val && this.router.navigate(['/signup'])));
  }
  constructor(private signupService: AuthService, private router: Router) {}
}

auth.service.ts:

export class AuthService {

    provider: any;
    account: any;
    accounts: any;
    web3: any;
    m_pub_key="";
    title = 'project2';
    is_signed_up:any;
    url = "http://localhost:8080/";
    val = of(false);

    constructor(private _appService: AppService, private zone: NgZone, private router: Router){
    }

    onReady():Observable<boolean> {
        // get initial account balance so it can be displayed
        // (async () => {
        if (typeof window.web3 !== 'undefined') {
            console.warn('Using injected web3');
            this.web3 = new Web3(window.web3.currentProvider);
        } else {
            // when running in browser, use incognito mode or log out of metamask
            console.warn('No web 3 detected, falling back to Ganache');
            this.provider = new Web3.providers.HttpProvider('http://localhost:7545');
            this.web3 = new Web3(this.provider);
        }
        window.ethereum.enable();


        this.web3.eth.getAccounts((err, accs) => {
          if (err != null) {
            console.log(err);
            alert('There was an error fetching your accounts.');
            return;
          }

          if (accs.length === 0) {
            alert('You are not connected to an Ethereum client.');
            return;
          }
          this.m_pub_key=accs[0];
          console.log(this.m_pub_key);
          // this.isSigned();
        });

        let data_send = JSON.stringify({
            'm_pub_key': this.m_pub_key
        });
            this._appService.post_method(this.url+"check_account", data_send).subscribe(
              data => {
                  this.is_signed_up=data["is_signed_up"];
                  console.log(this.is_signed_up+"***************8");
                  if(this.is_signed_up==1){
                    console.log("navigating to dash");
                    //this.router.navigate(['/dashboard']);
                    //this.http.get("/dashboard");
                    this.val = of(false);
                    // this.router.navigate(['/dashboard']);
                  }
                  else{
                    console.log("navigating to signup");
                    //this.router.navigate(['/signup']);
                    //this.http.get("/signup");
                    this.val = of(true);
                    // this.router.navigate(['/signup']);
                  }
                  console.log(this.val);
              },
                    // console.log(this.val);
              error => {
                  // console.log('post error');
              });
        // });
        console.log(this.val);
        return this.val
      }
}

【问题讨论】:

    标签: node.js angular angular2-routing


    【解决方案1】:

    第一个

    如果 Angular 这样做,你不应该处理后端中的每条路线

    app.get('/*', function(req,res) {
      res.sendFile(path.join( __dirname + '/dist/index.html'));
    });
    

    将此添加为您的 express 应用程序中的最后一条路由,会将其上方未处理的每个请求重定向到 angular 和 angular will use it's own router

    第二个,您需要为您的用例使用canActive guard 并在其中移动检查功能,如果用户未签名,它将重定向到signup页面,否则它将允许他通过,例如:

    @Injectable({
      providedIn: 'root',
    })
    export class AuthGuard implements CanActivate {
      canActivate(
        next: ActivatedRouteSnapshot,
        state: RouterStateSnapshot
      ): Observable<boolean> {
        console.log('AuthGuard#canActivate called');
        return this.signupService.isSigned()
                 .pipe(tap(val => !val && this.router.navigate(['/signup']))); // Navigate to signup when it returns false
      }
      constructor(private signupService: SignupService, private router: Router) {}
    }
    

    虽然SignupService 是一个服务,其方法isSigned() 返回一个Observable&lt;boolean&gt;

    您可能需要为 signup 页面路由创建另一个保护,以检查是否未签名并重定向到仪表板。

    更新: 您需要将整个代码转换为 Promise 或 observables,以处理异步函数,因为守卫接受 observable 和 Promise

    例如使用 observables 方式 你需要打开 getAcounts 函数来返回 observable 而不是依赖于回调,幸运的是 rxJs 有一个内置函数可以这样做它是 bindNodeCallback 你可以简单地做

    const getAccountsAsObservable = bindNodeCallback(
        callback => this.web3.eth.getAccounts(callback));
    

    现在您可以使用 rxJs 魔法通过管道链接代码 并且您还需要使用 pipe 处理错误并使其返回可观察到的 false。

    您的服务将类似于:

    onReady():Observable<boolean> {
      // ..
      const getAccountsAsObservable = Observable.bindNodeCallback(
        callback => this.web3.eth.getAccounts(callback));
    
      return getAccountsAsObservable()
        .pipe(
          switchMap(acc => {
            if (accs.length === 0) {
              alert('You are not connected to an Ethereum client.');
              // throw an error to skip to "cathError" pipe
              return throwError(new Error('You are not connected to an Ethereum client.'));
            }
            const m_pub_key=accs[0];
            console.log(this.m_pub_key);
            const data_send = JSON.stringify({
              'm_pub_key': m_pub_key
            });
            return of(data_send);
          }),
          switchMap(data_send => { 
            this._appService.post_method(this.url+"check_account", data_send)
          }),
          map(data => {
            this.is_signed_up=data["is_signed_up"];
            console.log(this.is_signed_up+"***************8");
            if(this.is_signed_up==1){
              console.log("navigating to dash");
              //this.router.navigate(['/dashboard']);
              //this.http.get("/dashboard");
              return false;
              // this.router.navigate(['/dashboard']);
            } else {
              console.log("navigating to signup");
              //this.router.navigate(['/signup']);
              //this.http.get("/signup");
              return true;
              // this.router.navigate(['/signup']);
            }
          }),
          catchError(err => {
            //handle error of getAccounts or post_method here
            console.log(err);
            alert('There was an error fetching your accounts.');
            return of(false);
          })
        )
    }
    

    你可以把整个事情变成 Promise 和/或使用 async/await 你可以转_appService.post_method.pipe(take(1)).toPromise 承诺

    【讨论】:

    • 感谢您的回答,但它仍然无法正常工作,请查看我在问题中所做的编辑并帮助我。我也按照你说的改变了我的后端文件,谢谢。
    • 它总是返回 false,因为 observables 是异步的,它总是会在 this._appService.post_method() 执行之前返回 of(false),看看 Observables 我会更新我的答案包括你应该如何重写你的服务
    • 问题是this._appService.post_method在设置data_send之前执行。 this.web3.eth.getAccounts 方法负责设置 data_send 对象,但执行该方法需要时间,因此返回值始终保持不变且不会更改。我找到了使用setTimeout 函数的解决方法,然后导航到相应的页面,它可以工作,但我认为这不是正确的方法。感谢您的帮助。
    • 抱歉一开始没有注意到,我现在更新了答案,如果你在看守中处理异步函数,你需要使用 promises 或 observables。看看吧。
    • 您好,抱歉回复晚了。所以,我尝试了你的代码,但它给了我以下错误:Property 'bindNodeCallback' does not exist on type 'typeof Observable'. Property 'length' does not exist on type '{}'. Cannot find name 'throwError'. Argument of type '(data_send: {}) =&gt; void' is not assignable to parameter of type '(value: {}, index: number) =&gt; ObservableInput&lt;{}&gt;'. Type 'void' is not assignable to type 'ObservableInput&lt;{}&gt;'. Argument of type '{}' is not assignable to parameter of type 'string'. Cannot find name 'catchError'. Did you mean 'RTCError'?
    猜你喜欢
    • 2015-03-20
    • 2020-03-11
    • 2018-04-07
    • 2020-11-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-02-12
    • 1970-01-01
    相关资源
    最近更新 更多