解释为什么 Express ‘app’ 和 ‘server’ 必须分开的原因?

参考回答

在使用 Express 开发应用时,建议将 app(Express 实例)和 server(HTTP 服务器实例)分开管理。这主要是为了提高代码的灵活性和模块化,使得应用程序更容易进行测试、扩展和维护。

具体原因包括:
1. 单元测试的需求:通过分离,可以直接测试 app,而无需依赖 HTTP 服务器。
2. 部署灵活性:允许在不同的服务器配置中复用 app 实例,而无需直接启动 HTTP 服务器。
3. 更好的控制:分离后,可以更精确地控制服务器的行为,例如跨服务共享 app


详细讲解与拓展

1. appserver 是什么?

  • app 是由 express() 创建的 Express 应用实例,负责定义路由、处理中间件和业务逻辑。
  • server 是由 Node.js 的 http.createServer()app.listen() 创建的 HTTP 服务器实例,负责监听指定端口并处理请求。

通常,开发中我们会用以下代码来启动 Express 应用:

const express = require('express');
const app = express();

app.get('/', (req, res) => {
  res.send('Hello World');
});

const server = app.listen(3000, () => {
  console.log('Server is running on port 3000');
});
JavaScript

尽管这段代码工作良好,但将 appserver 紧密绑定在一起可能会引发一些问题。


2. 分离 appserver 的好处

(1)单元测试

如果 appserver 耦合在一起,就无法单独测试 Express 的路由或中间件。例如:

const request = require('supertest');
const app = require('./app');

describe('GET /', () => {
  it('should return "Hello World"', async () => {
    const res = await request(app).get('/');
    expect(res.text).toBe('Hello World');
  });
});
JavaScript

上面示例中,测试只针对 app,而无需启动服务器实例。如果 appserver 耦合,则测试需要启动和关闭服务器,这会增加复杂性和运行时间。

(2)更灵活的部署

分离后,可以将 app 挂载到不同的 HTTP 服务器上(如 HTTPS、Socket 服务器)。例如:

const http = require('http');
const https = require('https');
const app = require('./app');

const httpServer = http.createServer(app);
const httpsServer = https.createServer({ key, cert }, app);

httpServer.listen(3000);
httpsServer.listen(443);
JavaScript

如果 appserver 不分离,上述代码会变得更加复杂,且不易扩展。

(3)中间件和路由的复用

分离 appserver 后,可以更方便地将 app 作为模块复用。例如:

const app = require('./app');
module.exports = app;
JavaScript

在其他服务中:

const app = require('./app');
const server = app.listen(4000);
JavaScript
(4)优雅的错误处理

分离后,可以在测试或实际运行环境中对服务器实例进行更好的控制。例如,优雅地关闭服务器:

const server = app.listen(3000, () => {
  console.log('Server is running on port 3000');
});

process.on('SIGINT', () => {
  server.close(() => {
    console.log('Server closed');
    process.exit(0);
  });
});
JavaScript

3. 最佳实践:分离实现方式

创建 app 模块

单独创建一个 app.js 文件,只负责配置 Express 应用:

const express = require('express');
const app = express();

app.get('/', (req, res) => {
  res.send('Hello World');
});

module.exports = app;
JavaScript
启动服务器

server.js 中加载 app 并启动 HTTP 服务器:

const app = require('./app');

const server = app.listen(3000, () => {
  console.log('Server is running on port 3000');
});
JavaScript
测试代码

测试只需引入 app 而不依赖 server

const request = require('supertest');
const app = require('./app');

describe('GET /', () => {
  it('should return "Hello World"', async () => {
    const res = await request(app).get('/');
    expect(res.statusCode).toBe(200);
    expect(res.text).toBe('Hello World');
  });
});
JavaScript

总结

appserver 分开是现代 Node.js 开发中的一种推荐实践。它不仅提高了代码的灵活性和模块化,还方便测试和部署。例如,测试中可以单独测试 app 实例,生产环境中可以挂载 app 到不同的服务器实例上。这种分离结构能够让代码更清晰、更易维护,同时适应更复杂的应用场景。

发表评论

后才能评论