【问题标题】:How to redirect all routes to index.html (Angular) in nest.js?如何将所有路由重定向到nest.js中的index.html(Angular)?
【发布时间】:2019-07-17 04:58:54
【问题描述】:

我正在制作 Angular + NestJS 应用程序,我想为所有路由发送 index.html 文件。

ma​​in.ts

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  app.useStaticAssets(join(__dirname, '..', 'frontend', 'dist', 'my-app'));
  app.setBaseViewsDir(join(__dirname, '..', 'frontend', 'dist', 'my-app'));
  await app.listen(port);
}

app.controller.ts

@Controller('*')
export class AppController {

  @Get()
  @Render('index.html')
  root() {
    return {};
  }
}

当我打开 localhost:3000/ 时,它运行良好,但如果我打开 localhost:3000/some_route,服务器会显示 500 internal error 并显示 Can not find html module。 我正在寻找为什么我会收到这个错误,每个人都说set default view engine like ejs or pug,但我不想使用一些引擎,我只想发送由 angular 构建的普通 html,而不像res.sendFile('path_to_file') 那样进行黑客攻击。请帮忙

【问题讨论】:

    标签: javascript node.js typescript express nestjs


    【解决方案1】:

    您只能将setBaseViewsDir@Render() 与车把(hbs)等视图引擎一起使用;但是,对于提供静态文件 (Angular),您只能使用 useStaticAssetsresponse.sendFile

    要从所有其他路线为index.html 提供服务,您有两种可能性:

    A) 中间件

    您可以创建一个执行重定向的中间件,请参阅article

    @Middleware()
    export class FrontendMiddleware implements NestMiddleware {
      resolve(...args: any[]): ExpressMiddleware {
        return (req, res, next) => {
          res.sendFile(path.resolve('../frontend/dist/my-app/index.html')));
        };
      }
    }
    

    然后为所有路由注册中间件:

    export class ApplicationModule implements NestModule {
      configure(consumer: MiddlewaresConsumer): void {
        consumer.apply(FrontendMiddleware).forRoutes(
          {
            path: '/**', // For all routes
            method: RequestMethod.ALL, // For all methods
          },
        );
      }
    }
    

    B) 全局错误过滤器

    您可以将所有NotFoundExceptions 重定向到您的index.html

    @Catch(NotFoundException)
    export class NotFoundExceptionFilter implements ExceptionFilter {
      catch(exception: HttpException, host: ArgumentsHost) {
        const ctx = host.switchToHttp();
        const response = ctx.getResponse();
        response.sendFile(path.resolve('../frontend/dist/my-app/index.html')));
      }
    }
    

    然后将其注册为您的main.ts 中的全局过滤器:

    app.useGlobalFilters(new NotFoundExceptionFilter());
    

    【讨论】:

    • 非常感谢!我认为如果我可以设置useStaticAssets 并从@Render() 返回文件会很容易。但它似乎比我想象的要复杂。
    【解决方案2】:

    2019 年 12 月 10 日的更新答案

    你需要创建中间件来发送react index.html

    创建中间件文件

    frontend.middleware.ts

    import { NestMiddleware, Injectable } from '@nestjs/common';
    import {Request, Response} from "express"
    import { resolve } from 'path';
    
    @Injectable()
    export class FrontendMiddleware implements NestMiddleware {
      use(req: Request, res: Response, next: Function) {
        res.sendFile(resolve('../../react/build/index.html'));
      }
    }
    

    中包含中间件

    app.module.ts

    import { FrontendMiddleware } from './frontend.middleware';
    import {
      Module,
      MiddlewareConsumer,
      RequestMethod,
    } from '@nestjs/common';
    import { AppController } from './app.controller';
    import { AppService } from './app.service';
    
    @Module({
      imports: [],
      controllers: [AppController],
      providers: [AppService],
    })
    export class AppModule {
      configure(frontEnd: MiddlewareConsumer) {
        frontEnd.apply(FrontendMiddleware).forRoutes({
          path: '/**', // For all routes
          method: RequestMethod.ALL, // For all methods
        });
      }
    }
    

    参考应用结构:

    enter image description here

    【讨论】:

    • 好的艾伦,但 OP 要求 Angular。我同意它是一样的。
    【解决方案3】:

    您还可以将 Cloud Functions for Firebase 与 Firebase 托管结合使用。 ma​​in.ts 中的内容非常好,使用这种方法,您甚至不需要控制器。你应该如下:

    1. index.html 重命名为index2.html。这对于渲染您的路由路径很重要,否则您将在所有路由上正常渲染,不包括根 /
    2. 更新angular.json 以获得以下"index": "apps/myapp/src/index2.html",(只需将index.html 更改为index2.html)。 注意:index.html 的路径对您来说可能不同,我使用的是Nx 工作区
    3. templatePath: join(BROWSER_DIR, 'index2.html'), 添加到NestJS 的ApplicationModule,很可能你在server 目录中将文件命名为app.module.ts

    像这样:

    @Module({
      imports: [
        AngularUniversalModule.forRoot({
          bundle: require('./path/to/server/main'), // Bundle is created dynamically during build process.
          liveReload: true,
          templatePath: join(BROWSER_DIR, 'index2.html'),
          viewsPath: BROWSER_DIR
        })
      ]
    })
    
    1. 初始化 Firebase Cloud Functions 和 Firebase Hosting,如何设置可以查看https://hackernoon.com/deploying-angular-universal-v6-with-firebase-c86381ddd445https://blog.angularindepth.com/angular-5-universal-firebase-4c85a7d00862

    2. 编辑您的 firebase.json

    它应该看起来像这样,或者至少是 hosting 部分。

    {
      "hosting": {
        "ignore": ["firebase.json", "**/.*", "**/node_modules/**"],
        "public": "functions/dist/apps/path/to/browser",
        "rewrites": [
          {
            "function": "angularUniversalFunction",
            "source": "**"
          }
        ]
      }
    } 
    
    1. 在您的 ma​​in.ts 中,您需要在您的服务器上设置 Cloud Functions。

    在极简主义的情况下,它会喜欢这样的东西:

    import * as admin from 'firebase-admin';
    import * as functions from 'firebase-functions';
    
    admin.initializeApp(); // Initialize Firebase SDK.
    const expressApp: Express = express(); // Create Express instance.
    
    // Create and init NestJS application based on Express instance.
    (async () => {
      const nestApp = await NestFactory.create<NestExpressApplication>(
        ApplicationModule,
        new ExpressAdapter(expressApp)
      );
      nestApp.init();
    })().catch(err => console.error(err));
    
    // Firebase Cloud Function for Server Side Rendering (SSR).
    exports.angularUniversalFunction = functions.https.onRequest(expressApp);
    

    使用这种方法,您不必关心 NestJS 端的路由。您可以在 Angular 端设置所有内容,仅此而已。 Angular 负责路由。您可能注意到这是服务器端渲染 (SSR),但是可以结合使用 NestJS + Cloud Functions for Firebase 将所有路由重定向到 index.html(或更准确地说是 index2.html)。另外,您还有“免费”的 SSR :)

    要展示的项目:

    1) Angular + Angular Universal (SSR) + Cloud Functions for Firebase:https://github.com/Ismaestro/angular8-example-app(缺少 NestJS)。

    2) Angular + NestJS:https://github.com/kamilmysliwiec/universal-nest(缺少 Firebase 的云函数)。

    【讨论】:

      猜你喜欢
      • 2016-05-04
      • 1970-01-01
      • 2018-02-14
      • 1970-01-01
      • 2012-07-22
      • 1970-01-01
      • 2019-02-02
      • 2013-02-15
      • 2015-06-16
      相关资源
      最近更新 更多