簡體   English   中英

將 Nest.js 應用程序導入為簡單的 Express 中間件

[英]Import a Nest.js app as a simple Express middleware

我有一個 Nestjs 應用程序(一個 Rest API),我想將它作為一個簡單的 Express 中間件(不是 Nest 中間件)導入另一個節點模塊。 實際上,我仍然無法使其正常工作。

// 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>

嵌套組件版本:

- @nestjs/common: "^6.10.14"
- @nestjs/core: "^6.10.14"
- @nestjs/platform-express: "^6.10.14"
- express: "^4.16.4"

雖然我不容忍將 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();
  }
}

應用服務.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 {}

服務器.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 實例,這可能真的會混淆節點服務器。

4) 如果您想知道,作為[Object object]出現的錯誤是標准的 Express 錯誤Cannot GET / 不知道為什么它被奇怪地序列化,但是捕獲中的JSON.stringify()幫助解決了它。

總的來說,我不推薦這種方法,可以這樣做。

我知道這不完全是問題的答案,但我只想留下一個使用這個中間件的例子。

就我的情況而言,我認為把所有東西都放在巢里是可以的,而不是把巢放在快遞里。 我需要把我所有的標准快遞應用程序,與節點一起工作,沒有特殊條件,只需加入2,這就是我的場景。

我只是采用了全局設置,如 body-parser 和 dotenv,並將其放入我的主文件中。

src/main.ts

import dotenv from 'dotenv'
import bodyParser from 'body-parser'
import { useExpress } from './workspaces/poc/server'
import { TodoModule } from './workspaces/todo/todo.module'
import { NestFactory } from '@nestjs/core';

// was in src/workspaces/my-legacy-app/server.ts
dotenv.config()

async function bootstrap() {
  const app = await NestFactory.create(TodoModule);
  app.use(bodyParser.json());

  // was in src/workspaces/my-legacy-app/server.ts
  // also did not know how to resolve the issue of types, so use "any"
  useExpress(app.getHttpAdapter() as any)

  await app.listen(3000,() => {
    console.info(`App runnning on port: ${3000}`)
  });
}
bootstrap();

我的舊遺留主文件

src/workspaces/legacy-app/server.ts

import { validatorMiddleware } from './middlewares/validator.middleware'
import { logMiddleware } from './middlewares/log.middleware'
import { userRouter } from './routes/user.route'
import { Express } from 'express'

export function useExpress(server: Express){

  server.use(validatorMiddleware)
  server.use(logMiddleware)
  server.use('/user', userRouter)
  
  // commented because the server will go up here more, but just to show that it was the same way as in express
  // server.listen(
  //   process.env.PORT,
  //   () => console.log(`Server is running on port ${process.env.PORT ?? 3000}`)
  // )
}

基於Jay McDoniel的回答略有不同的方法。

我需要在 Express“父”應用程序的特定路徑(例如“/api/v2”)處公開 NestJS 應用程序,並將“父”應用程序傳遞給 ExpressAdapter(通過app.middleware.ts )導致問題。 另外,我不想在每個請求上都啟動一個新的 Nest 實例。

除了app.middleware.ts文件,您可以定義一個函數(即getExpressApp )來創建一個新的 express 實例,使用該實例來創建和初始化 NestJS 應用程序並返回實際的 express 實例。 然后,您可以在main.ts中使用該函數,如下所示:

應用程序.express.ts

import { NestFactory } from '@nestjs/core';
import { ExpressAdapter } from '@nestjs/platform-express';
import * as express from 'express';
import { AppModule } from './app.module';

/** Creates and initializes a NestJS app and returns its raw express instance */
export const getExpressApp = () => {
  const app = express();
  return NestFactory.create(AppModule, new ExpressAdapter(app))
    .then((nestApp) => nestApp.init())
    .then(() => app);
};

main.ts

import * as express from 'express';
import {getExpressApp} from './app.express';

const parentApp = express();
// ... parentApp configuration

getExpressApp()
  .then(childApp => {
    parentApp.use('/api/v2', childApp);
  })
  .catch(() => {
    console.error('Error when mounting Nest server');
  })
  .finally(() => {
    parentApp.listen(3300)
  });

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM