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
3 Réponses :
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:
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')));
}
}
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')));
};
}
}
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.
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:
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 / . 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 >. 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);
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
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": "**"
}
]
}
}
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).
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:
Ok Alan, mais OP demande Angular. Je suis d'accord que c'est pareil.