javascript - 將 Nest.js 應用作為簡單的 Express 中介軟體導入
我有一個 Nestjs 應用程式(一個 Rest API),我想把它作為一個簡單的 Express 中介軟體(而不是 Nest 中介軟體)導入到另一個 node 模組中。實際上,我仍然無法使它運作。
// main.ts
// => The main file of my Nest app, this one is working properly.
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
await app.listen(3000);
}
bootstrap();
// app.middleware.ts
import {Injectable, NestMiddleware} from '@nestjs/common';
import {NestFactory} from '@nestjs/core';
import {AppModule} from './app.module';
import {ExpressAdapter} from '@nestjs/platform-express';
import express, {Request, Response} from 'express';
const bootstrap = async () => {
const expressApp = express();
const adapter = new ExpressAdapter(expressApp);
const app = await NestFactory.create(AppModule, adapter);
await app.init();
return app;
};
@Injectable()
export class AppMiddleware implements NestMiddleware {
use(req: Request, res: Response, next: Function) {
return bootstrap();
}
}
// express-app.ts
// => Here I'm trying to load my app through a simple Express middleware, but it doesn't works.
import express from 'express';
import { AppMiddleware } from './app.middleware';
const app = express();
const PORT = process.env.PORT || 3000;
app.use((req, res, next) => {
const app = new AppMiddleware().use(req, res, next);
app.then(next);
});
app.listen(PORT, () => {
console.log(`app running on port ${PORT}`);
});
當我從 main.ts 運行我的應用程式時,它運作正常(所有路由都能使用,並且我獲得了正確的資料)。但是,當我嘗試透過 express-app.ts 運行應用程式時,所有路由似乎都能運作(它們會顯示在終端中),但無論如何,我得到的都是這個錯誤,而不是返回 JSON 物件:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Error</title>
</head>
<body>
<pre>[object Object]</pre>
</body>
</html>
Nest 組件版本:
- @nestjs/common: "^6.10.14"
- @nestjs/core: "^6.10.14"
- @nestjs/platform-express: "^6.10.14"
- express: "^4.16.4"
答案#1
雖然我不認可將 Nest 作為中介軟體本身來使用,但這是可行的。使用 nest new express-server -p npm 的基本設置來創建新的 NestJS 應用,並使用 src/server.ts 設置一個小型的 express 服務器,我能夠使以下代碼工作。
app.middleware.ts
import { Injectable, NestMiddleware } from '@nestjs/common';
import { NestFactory } from '@nestjs/core';
import { ExpressAdapter } from '@nestjs/platform-express';
import { AppModule } from './app.module';
const bootstrap = async (express: Express.Application) => {
const app = await NestFactory.create(AppModule, new ExpressAdapter(express));
await app.init();
return app;
}
@Injectable()
export class AppMiddleware implements NestMiddleware {
constructor(private expressInstance: Express.Application) {}
use(req: any, res: any, next: () => void) {
console.log('In Nest middleware');
return bootstrap(this.expressInstance);
}
}
app.controller.ts
import { Controller, Get } from '@nestjs/common';
import { AppService } from './app.service';
@Controller()
export class AppController {
constructor(private readonly appService: AppService) {}
@Get()
getHello(): string {
return this.appService.getHello();
}
}
app.service.ts
import { Injectable } from '@nestjs/common';
@Injectable()
export class AppService {
getHello(): string {
return 'Hello World!';
}
}
app.module.ts
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
@Module({
imports: [],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
server.ts
import * as express from 'express';
import { AppMiddleware } from './app.middleware';
const app = express();
app.use((req, res, next) => {
const nest = new AppMiddleware(app).use(req, res, next);
nest.then(() => {
next();
}).catch(err => {
console.log(JSON.stringify(err));
next();
});
});
app.listen(3000, () => {
console.log('Listening on port 3000');
});
建置指令
npm run build
mapped to nest build
開始指令
node dist/server.js
測試指令
▶ curl http://localhost:3000
Hello World!
主控台日誌
Listening on port 3000
In Nest middleware
[Nest] 24235 - 02/18/2020, 8:05:44 PM [NestFactory] Starting Nest application...
[Nest] 24235 - 02/18/2020, 8:05:44 PM [InstanceLoader] AppModule dependencies initialized +15ms
[Nest] 24235 - 02/18/2020, 8:05:44 PM [RoutesResolver] AppController {/}: +3ms
[Nest] 24235 - 02/18/2020, 8:05:44 PM [RouterExplorer] Mapped {/, GET} route +2ms
[Nest] 24235 - 02/18/2020, 8:05:44 PM [NestApplication] Nest application successfully started +2ms
記住幾件事:
1) 使用這種方法,除非你緩存你的 Nest 伺服器,否則你會在每次請求時建立一個新的 Nest 伺服器,隨著你在 Nest 方面的發展,這只會讓你的專案變得更慢。
2) 你也可以將現有的 Express 伺服器傳給 ExpressAdapter,就像你在現有程式碼中部分做的那樣,並從 Nest 的 app.listen() 函式啟動伺服器。只要確保移除任何錯誤處理中介軟體,因為它會開始與 Nest 處理回應的方式發生衝突。你應該將這些函式移到 ExceptionFilters。
3) 你在 app.middleware 中的一個錯誤是,你不僅在每次調用時創建了一個新的 Nest 實例,還創建了一個新的 express 實例,這可能真的會讓 node 伺服器感到困惑。
4) 如果你想知道,出現為 [Object object] 的錯誤其實是一個標準的 Express 錯誤 Cannot GET /。不知道為什麼它會被奇怪地序列化,但在 catch 裡使用 JSON.stringify() 幫助解決了這個問題。
總體而言,我不建議這種方法但是這是可行的。
答案#2
@Etienne 你的 bootstrap 函數實際上沒問題,你可以直接在 express-app.ts 中使用它。優點:
- 每個請求不建立新的 Nest 實例
- 每個 Express 應用的獨立配置
app.middleware.ts
import {NestFactory} from '@nestjs/core';
import {AppModule} from './app.module';
import {ExpressAdapter} from '@nestjs/platform-express';
import express from 'express';
export const bootstrap = async () => {
const expressApp = express();
const adapter = new ExpressAdapter(expressApp);
const app = await NestFactory.create(AppModule, adapter);
await app.init();
return app;
};
express-app.ts
import express from 'express';
import { bootstrap } from './app.middleware';
const app = express();
const PORT = process.env.PORT || 3000;
bootstrap().then(expressApp => {
app.use(expressApp);
app.listen(PORT, () => {
console.log(`app running on port ${PORT}`);
});
});
