原文出处: Setting Up 5 Useful Middlewares For An Express API
最近在学习 Node+Express
,由于个人水平有限以及令人捉急的英文,无法完美的呈现出作者的意思,也少不了错误,还请各位童鞋多多指正。本文纯粹是给自己在学习 Node + Express
的路上做一个记录。并没有多少技术含量,对 Express
开发很熟悉的人可以绕道。
如果你正使用 Node.js
和 Express
构建 Api
,这篇文章将告诉你提高 RESRTFul Api
安全和性能的一些诀窍和技巧。
在这篇文章中,我们将创建一个简单的只有一个端口的 Express Api
的示例。现在开始构建我们的项目吧。
$ mkdir my-api $ cd my-api $ npm init
npm init
对我们的项目可以快速设置一些简短的描述,主入口文件和我们将要安装的一些模块,并生成一个 package.json
文件。用你自己的方式随时回复每个问题。
Express
框架。 $ npm install express --save
到此 Express
框架已经安装成功。开始我们的一个小而简单的 API
代码并开始创建 index.js
文件。
var express = require('express'); var app = express(); app.get('/', function(req, res) { res.json({status: 'My Api is alive!'}); }); app.listen(3000, function() { console.log('My Api is running...'); }); module.exports = app;
启动服务器,测试是否正常
$ node index.js
在浏览器地址栏内输入 localhost:3000
再下一节中,我们将要通过一个小的功能来探讨一些有用的可以提高应用程序的中间件。
CORS
简介 为防止你不了解, CORS
全称: Cross-origin resource sharing 跨域资源共享
,是一种重要的 HTTP
机制。它负责准许跨域或同步请求。
在实际项目中,服务器端 CORS
只包含在 HTTP's
的头部。这些头文件可以告知哪个域可以请求API,哪些http方法是被允许的。最主要的是哪些终端可以跨域共享公共的应用程序。
Api
内启用 CROS
在我们正在开发的 API
中,需要启用 CORS
的中间件使之成为公共的端口,可用于任何客户端共享数据。这意味着某些客户端可以访问我们的 Api
。
cors
模块来启用它 $ npm install cors --save
index.js
内添加中间件 app.use(cors())
var express = require('express'); var cors = require('cors'); var app = express(); app.use(cors()); app.get('/', function(req, res) { res.json({status: 'My Api is alive!'}); }); app.listen(3000, function() { console.log('My Api is running...'); }); module.exports = app;
只有使用 cors()
中间件才会释放出我们 Api
的访问权限。最好是控制哪些客户端可以进行连接,哪些方法可以使用。最主要的是,必须要在请求的时候加入到头部。在我们这个例子中仅需要设置三个属性: orgin(访问域)、method(访问方法)、alloweHeaders(请求头)。
因此,向 app.use(cors())
添加一些参数。
var express = require('express'); var cors = require('cors'); var app = express(); app.use(cors({ origin: ['http://localhost:3001'], methods: ['GET', 'POST'], alloweHeaders: ['Conten-Type', 'Authorization'] })); app.get('/', function(req, res) { res.json({status: 'My Api is alive!'}); }); app.listen(3000, function() { console.log('My Api is running...'); }); module.exports = app;
现在我们可以在流览器中访问 localhost:3001 ,该客户端应用程序只能通过头为: Content-Type
和 Authorization
进行 GET
或 POST
请求。
CORS
学习 CORS
的目的是为了了解它的请求头,更重要的是学会如何自定义 API
规则 ,建议你完整读完这篇文章: developer.mozilla.org/en-US/docs/Web/HTTP/Access control CORS
在本节中,设置我们的应用程序报告并把用户的请求生成日志文件。要做到这一点,需要使用 服务器生成请求日志的中间件- morgan
模块。
$ npm install --save morgan
然后,在上面引用 app.use(morgan('common'))
以记录所有的请求。
var express = require('express'); var cors = require('cors'); var morgan = require('morgan'); var app = express(); app.use(morgan('common')); app.use(cors({ origin: ['http://localhost:3001'], methods: ['GET', 'POST'], alloweHeaders: ['Conten-Type', 'Authorization'] })); app.get('/', function(req, res) { res.json({status: 'My Api is alive!'}); }); app.listen(3000, function() { console.log('3000 My Api is running...'); }); app.listen(3001, function() { console.log('3001 My Api is running...'); }); module.exports = app;
重启服务器,多次访问 localhost:3000 , 验证是否生成日志。
cluster
模块配置并行处理 大家都知道 Node.js
是一个单线程的运行的。很多开发者不认同这一点,把它当回事,进而影响了他们的学习兴趣。然而,尽管他是单线程的,但通过一些准备还是可以做到并行运行的。可以通过内置的 cluster
模块做到这一点。
它基本上在分布式方面是一个新的初始化应用并且该模块可以在活动集群间共享同一个网络端口。它可以决定创建的进程数量。但一个更好的做法是实例化基于多个服务器的处理器或者相当数量的多核处理器。
例如:如果我有一个八核单处理器,就可以实例化创建八个集群网络的八个进程;但是,如果我有四个八核处理器,就可以创建32个活动集群网络。
若要确保集群可以分布式和有组织的工作,父进程是必须存在的(也称之为集群主机)。 因为它是负责平衡其他集群之间的并行处理,将负载分发到其他进程叫做子进程(或者 cluster slave
)。作为一个开发者在nodejs上抽象的分配执行这种技术,是非常容易的。
另一个优点是 cluster
是独立的。也就是说,即使一个 cluster
出现故障,其他将会继续工作。然而,有必要管理实例和手动关闭集群确保集群可以回归。
根据这些概念,我们将在实践中采用 cluster
。
在根目录下创建 clusters.js
文件,代码如下:
var cluster = require('cluster'); var os = require('os'); const CPUS = os.cpus(); if (cluster.isMaster) { CPUS.forEach(function () { cluster.fork(); }); cluster.on('listening', function(worker) { console.log('Cluster %d connected', worker.process.pid); }); cluster.on('disconnect', function(worker) { console.log('Cluster %d disconnected', worker.process.pid); }); cluster.on('exit', function(worker) { console.log('Cluster %d dead', worker.process.pid); // Ensuring a new cluster will start if an old one dies cluster.fork(); }); } else { require("./index.js"); }
$ node clusters.js
执行此命令后,运行应用程序分配到集群,在终端上你会看到不止一次的出现 My Api is running...
这条消息,就像如下图所示:
基本上我们都需要加载 cluster
模块,并通过 cluster.isMaster
来验证当前进程是不是主进程。一旦确定是主进程通过 CPUS.forEach(function() { cluster.fork() })
函数循环所有的内核处理器forking一个新的子进程。
当 if(cluster.isMaster)
条件的时候并不适合(在这种情况下子进程)创建一个新的进程。所以,通过 require("./index.js")
为子进程启动应用服务器。
另外,通过主进程创建了一些事件也都包括在内。在代码的最后一个列子,我们只用下面列出的主要事件:
listening:进程发生时监听的端口,当前情况下,哦们的应用程序正在监听3000端口
disconnect: 一个进程从进程网络中断开时发生
exit: 一个进程在操作系统关闭时发生
很多事情都可以进行探讨关于Node.js 的集群发展。在这里我们只采用一点足以运行并行处理。万一你要执行更加详细的clusters,建议您要阅读文档: nodejs.org/api/cluster.html 。
GZIP
中间件合并请求 为了使请求更轻,加载速度更快。我们可以用采用另外一个中间件负责压缩响应的json数据和静态文件为GZIP格式,可以设置一些浏览器的兼容性。我们做的简单但却很重要的是模块压缩。
$ npm install --save compression
index.js
文件内。 var express = require('express'); var cors = require('cors'); var morgan = require('morgan'); var compression = require('compression'); var app = express(); app.use(morgan('common')); app.use(cors({ origin: ['http://localhost:3001'], methods: ['GET', 'POST'], alloweHeaders: ['Conten-Type', 'Authorization'] })); app.use(compression()); app.get('/', function(req, res) { res.json({status: 'My Api is alive!'}); }); app.listen(3000, function() { console.log('My Api is running...'); }); module.exports = app;
SSL
支持使用 HTTPS
现如今,创建服务器和客户端之间的安全连接的安全应用程序。要做到这一点,许多应用程序购买和使用的安全证书,以确保SSL(安全套接层)通过HTTPS协议连接。
为了实现 HTTPS
协议链接,必须要购买一个生产环境使用的数字证书。假设你已经有一个了,在项目根目录下创建两个文件(文件扩展名一个为 .key
,一个为 .cert
)。然后,我们启用 https
模块来实现 HTTPS
服务器,通过 fs
模块 来打开和读取证书文件 my-api.key
和 my-api.cert
作为凭证参数来启动我们服务器的 HTTPS
模式。我们可以把 app.listen()
函数改为 https.createServer(credentials, app).listen()
函数来实现这一功能。
API
代码看看 var express = require('express'); var cors = require('cors'); var morgan = require('morgan'); var compression = require('compression'); var fs = require('fs'); var https = require('https'); var app = express(); app.use(morgan('common')); app.use(cors({ origin: ['http://localhost:3001'], methods: ['GET', 'POST'], alloweHeaders: ['Conten-Type', 'Authorization'] })); app.use(compression()); app.get('/', function(req, res) { res.json({status: 'My Api is alive!'}); }); var credentials = { key: fs.readFileSync('my-api.key', 'utf8'), cert: fs.readFileSync('my-api.cert', 'utf8') }; https.createServer(credentials, app) .listen(3000, function() { console.log('My Api is running...'); }); module.exports = app;
恭喜!现在您的应用程序在一个安全的协议下运行,不用再担心数据被拦截了。请注意,实际项目中需要一个有效的数字证书来实现这种需求。所以不要忘了买一个在生产环境内你的重要API。
如何生成签名证书
# 生成 my-api.key $ openssl genrsa -des3 -out my-api.key 1024 # 生成签名请求的csr 文件 $ openssl req -new -key my-api.key -out my-api.csr # 对自己证书进行签名,签名有效期是365天 $ openssl x509 -req -days 365 -in my-api.csr -signkey my-api.key -out my-api.cert # 去除签名文件的password $ cp my-api.key my-api.key.orig $ openssl rsa -in my-api.key.orig -out my-api.key
https://localhost:3000
纵观我们的API开发,有一个非常重要的模块就是处理攻击 HTTP/HTTPS 协议的安全中间件。此模块称之为安全帽一共有九个内部组件,负责下列http 设置:
Content Security Policy
防止XSS 攻击 综上所述,即使你对 http
安全性不适很明白,你可以使用 helmet
模块,它会增加一个简单的界面让你的web应用程序应对多种类型的攻击。
安装 helmet
依赖
$ npm install --save helmet
我们很容易的通过 app.use(helmet())
引入 helmet
模块所提供的中间件, 最大程度的确保我们 API
的安全性。
var express = require('express'); var cors = require('cors'); var morgan = require('morgan'); var compression = require('compression'); var fs = require('fs'); var https = require('https'); var helmet = require('helmet'); var app = express(); app.use(morgan('common')); app.use(helmet()); app.use(cors({ origin: ['http://localhost:3001'], methods: ['GET', 'POST'], alloweHeaders: ['Conten-Type', 'Authorization'] })); app.use(compression()); app.get('/', function(req, res) { res.json({status: 'My Api is alive!'}); }); var credentials = { key: fs.readFileSync('my-api.key', 'utf8'), cert: fs.readFileSync('my-api.cert', 'utf8') }; https.createServer(credentials, app) .listen(3000, function() { console.log('My Api is running...'); }); module.exports = app;
打开浏览器控制台,并在 Networks
菜单中,您可以详细的查看GET/ 请求的数据。你会看到头部有新的名目。类似下面这样图像上的:
所有的数据通过 cluster 并行处理、通过 GZIP
压缩优化数据传输。通过 CORS
启用受限制的web客户端、所有的请求都会通过 morgan
模块记录到日志文件。现在你的 API
通过这些最佳的安全做法,让你免受一些常见的攻击。
随时在你新的 Express
项目中引用这些小的 API
吧