如何将所有路由重定向到nest.js中的index.html(Angular)?

Posted

技术标签:

【中文标题】如何将所有路由重定向到nest.js中的index.html(Angular)?【英文标题】:How to redirect all routes to index.html (Angular) in nest.js? 【发布时间】: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') 那样进行黑客攻击。请帮忙

【问题讨论】:

【参考方案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 中的内容非常好,使用这种方法,您甚至不需要控制器。你应该如下:

    index.html 重命名为index2.html。这对于渲染您的路由路径很重要,否则您将在所有路由上正常渲染,不包括根 /。 更新angular.json 以获得以下"index": "apps/myapp/src/index2.html",(只需将index.html 更改为index2.html)。 注意:index.html 的路径对您来说可能不同,我使用的是Nx 工作区。 将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
    )
  ]
)

    初始化 Firebase Cloud Functions 和 Firebase Hosting,如何设置可以查看https://hackernoon.com/deploying-angular-universal-v6-with-firebase-c86381ddd445 或https://blog.angularindepth.com/angular-5-universal-firebase-4c85a7d00862

    编辑您的 firebase.json

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


  "hosting": 
    "ignore": ["firebase.json", "**/.*", "**/node_modules/**"],
    "public": "functions/dist/apps/path/to/browser",
    "rewrites": [
      
        "function": "angularUniversalFunction",
        "source": "**"
      
    ]
  
 
    在您的 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 (s-s-r).
exports.angularUniversalFunction = functions.https.onRequest(expressApp);

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

要展示的项目:

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

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

【讨论】:

以上是关于如何将所有路由重定向到nest.js中的index.html(Angular)?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Laravel 4 中将一个路由重定向到 HTTPS,将所有其他路由重定向到 HTTP?

index.php到哪个路由重定向?

Next.js:我们如何将动态路由重定向到静态页面?

.htaccess - 将所有 URL + 现有目录重定向到 index.php

Nginx 总是将 Laravel 路由重定向到默认的根 index.php 页面

当所有路由根据条件动态加载时如何重定向到路由?