4
votes

Comment rediriger toutes les routes vers index.html (Angular) dans nest.js?

Je crée l'application Angular + NestJS et je souhaite envoyer un fichier index.html pour toutes les routes.

main.ts

@Controller('*')
export class AppController {

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

app.controller.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);
}

Cela fonctionne bien pendant que j'ouvre localhost: 3000 / , mais si j'ouvre localhost: 3000 / some_route le serveur tombe avec une erreur interne 500 et dit Impossible de trouver le module html . Je cherchais pourquoi j'obtiens cette erreur et tout le monde dit définir le moteur de vue par défaut comme ejs ou pug , mais je ne veux pas utiliser certains moteurs, je veux juste envoyer du HTML simple construit par angular sans piratage comme res.sendFile ('path_to_file') . Veuillez aider


0 commentaires

3 Réponses :


3
votes

Vous ne pouvez utiliser setBaseViewsDir et @Render () qu'avec un moteur de visualisation tel que les guidons (hbs); pour servir des fichiers statiques (Angular), cependant, vous ne pouvez utiliser que useStaticAssets et response.sendFile .

Pour servir index.html depuis toutes les autres routes, vous avez quelques possibilités:

A) Middleware

Vous pouvez créer un middleware qui effectue la redirection, voir ce article :

app.useGlobalFilters(new NotFoundExceptionFilter());

puis enregistrez le middleware pour toutes les routes:

@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')));
  }
}

B) Filtre d'erreur global

Vous pouvez rediriger tous les NotFoundExceptions dans votre 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
      },
    );
  }
}

puis enregistrez-le comme filtre global dans votre main. ts :

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


1 commentaires

Merci beaucoup! J'ai pensé que cela pourrait être facile si je pouvais simplement définir useStaticAssets et renvoyer des fichiers depuis @Render () . Mais cela semble plus complexe que je ne le pensais.



0
votes

Vous pouvez également utiliser Cloud Functions pour Firebase avec Firebase Hosting. Ce que vous avez dans main.ts est parfaitement bien, avec cette approche, vous n'avez même pas besoin d'un contrôleur. Vous devez procéder comme suit:

  1. Renommez index.html en index2.html . Ceci est important pour rendre le chemin de votre route, sinon le rendu fonctionnera correctement sur toutes les routes, à l'exception de la racine / .
  2. Mettez à jour angular.json pour avoir le "index" suivant: "apps / myapp / src / index2.html", (il suffit de changer index.html à index2.html ). Remarque: le chemin d'accès à l'index.html peut être différent pour vous, j'utilise l ' espace de travail Nx strong >.
  3. Ajoutez templatePath: join (BROWSER_DIR, 'index2.html'), au ApplicationModule de NestJS, vous nommez probablement le fichier comme app.module.ts dans un répertoire du serveur .

Comme ceci:

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);
  1. Initialisez Firebase Cloud Functions et Firebase Hosting, pour savoir comment configurer cela, vous pouvez consulter https://hackernoon.com/deploying-angular-universal-v6-with-firebase-c86381ddd445 ou https://blog.angularindepth.com/angular-5-universal-firebase-4c85a7d00862

  2. Modifiez votre firebase.json

Cela devrait ressembler à ça, ou du moins à la partie hébergement .

{
  "hosting": {
    "ignore": ["firebase.json", "**/.*", "**/node_modules/**"],
    "public": "functions/dist/apps/path/to/browser",
    "rewrites": [
      {
        "function": "angularUniversalFunction",
        "source": "**"
      }
    ]
  }
} 
  1. Dans votre main.ts , vous devez configurer Cloud Functions sur votre serveur.

Dans un cas minimaliste, il aimerait quelque chose comme ça:

@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
    })
  ]
})

Avec cette approche, vous n'avez pas à vous soucier des itinéraires du côté NestJS. Vous pouvez tout configurer du côté angulaire, et c'est tout. Angular s'occupe du routage. Comme vous l'avez probablement remarqué, il s'agit du rendu côté serveur (SSR), mais la redirection de toutes les routes vers index.html (ou plus précisément index2.html ) peut être effectuée à l'aide de NestJS + Fonctions Cloud pour Firebase en conjonction. De plus, vous avez un SSR "gratuit" :)

Projets à présenter:

1) Fonctions angulaires + angulaires universelles (SSR) + cloud pour Firebase: https://github.com/Ismaestro/angular8-example-app (NestJS manquant).

2) Angulaire + NestJS: https://github.com/kamilmysliwiec/universal-nest a> (il manque Cloud Functions pour Firebase).


0 commentaires

1
votes

Réponse mise à jour du 10 décembre 2019

Vous devez créer un middleware pour envoyer le react index.html

Créer un fichier middleware

frontend.middleware.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
    });
  }
}

Inclut le middleware dans

app.module. 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'));
  }
}

Structure de l'application pour référence:

entrez la description de l'image ici


1 commentaires

Ok Alan, mais OP demande Angular. Je suis d'accord que c'est pareil.