FrankKai / FrankKai.github.io

FE blog
https://frankkai.github.io/
363 stars 39 forks source link

Nodejs的中间件层-Connect #68

Closed FrankKai closed 6 years ago

FrankKai commented 6 years ago

面试过程中被问到:是否了解Nodejs的中间件? 我一直在用别人写好的中间件,比如koa-body,koa-cors等等,声明实例并且use。 对于实现原理也只是简单看过对朴灵的《深入浅出nodejs》中的中间件部分。(比较深奥,理解吃力)

面试完之后,在《Node.js实战》中了解到Connect中间件框架,对于原理讲的比较清楚,但是了解到这是一个开源框架,更新进度要比书领先,因此我将对其README进行翻译,从而汲取最新版本的Connect框架中的营养。 https://github.com/senchalabs/connect

FrankKai commented 6 years ago

Connect

Connect是一个使用插件进行扩展HTTP 服务器的Nodejs框架,通常我们将这些插件称作"中间件"。

var connect = require('connect');
var http = require('http');

var app = connect();

// 压缩/解压 然后输出响应
var compression = require('compression');
app.use(compression());

// 在浏览器cookie中存储session状态
var cookieSession = require('cookie-session');
app.use(cookieSession({
    keys: ['secret1', 'secret2']
}));

// 解析编码请求体到req.body
var bodyParser = require('body-parser');
app.use(bodyParser.urlencoded({extended: false}));

// 响应所有请求
app.use(function(req, res){
  res.end('Hello from Connect!\n');
});

// 创建node.js http 服务器并监听端口
http.createServer(app).listen(3000);

入门

Connect 是一个简单的框架,它主要负责粘合各种中间件去处理请求。

安装

$ npm install connect

创建一个app

主组件是一个Connect应用。这将存储所有的中间件,并且这个主组件本身也是一个函数。

var app = connect();

中间件的使用

Connect的核心是"使用"中间件。中间件被添加到"stack"中,栈里传入请求并且逐一执行每个中间件,直到某一个中间件内部不再调用next()

app.use(function middleware1(req, res, next) {
  // middleware 1
  next();
});
app.use(function middleware2(req, res, next) {
  // middleware 2
  next();
});

挂载中间件

.use()方法也接收一个额外的可选的路径字符串,这个字符串与输入的请求url的开始处匹配。从而实现下面这样基本的路由:

app.use('/foo', function fooMiddleware(req, res, next) {
  // req.url的开头是 "/foo"
  next();
});
app.use('/bar', function barMiddleware(req, res, next) {
  // req.url的开头是"/bar"
  next();
});

异常中间件

有一些特殊的异常处理中间件。这种类型的中间件通常会接收4个参数。当中间件传递一个error到next时,应用将继续寻找在当前中间件之后已声明的异常中间件并且调用它,跳过其他的之前的异常中间件和之后的非异常中间件。

// 通常的中间件
app.use(function (req, res, next) {
  // 我有一个异常
  next(new Error('boom!'));
});

// 发生在中间件中的为error定义的异常中间件
// 在这里定义了onerror异常中间件函数
app.use(function onerror(err, req, res, next) {
  // 一个错误发生了!
});

从应用程序创建一个服务器

最后一步实质上是在一个服务器上使用Connect应用。.listen方法是一个很方便的启动HTTP服务器的接口(),实质上与你正在运行的Node.js中的http.Serverlisten方法相同。

var server = app.listen(port);

应用本身仅仅是一个需要传入3个参数的函数,所以它可以传递到Node.js的.createServer()

var server = http.createServer(app);

中间件

这些中间件和库通常由Connect/Express团队维护:

除cookie-session之外,大多数中间件与Connect 2.x版本的等价接口。

一些之前版本的中间件已经不被Connect/Express团队提供支持,被可选择性的模块所替代,或者被更好地模块替代。可以改用其中一种替代方法:

查看 http-framework 提交其他稳定的http库。

API

Connect API非常简单,足以去创建一个app并且添加一个middleware链。 当connect模块被引入进来以后,调用函数以后会构建并且返回一个新的应用。

// 引入模块
var connect = require('connect')

// 创建应用
var app = connect()

app(req, res[, next])

app自身是一个函数。它其实只是app.handle的别名。

app.handle(req, res[, out])

函数调用时将运行middleware栈中的给定的http请求(req)和http响应(res)对象。一个可选的函数out,如果请求或者error不被中间件调用栈处理的话,它会被调用。

app.listen([...])

启动应用去监听请求。这个方法将在内部新建一个Node.js HTTP服务器并且调用.listen()方法。

这个一种替代Node.js的原生server.listen()方法,所以请查阅Node.js文档去查阅不同的变体。最常见的签名是app.listen(port).

app.use(fn)

在一个应用程序中使用一个函数,当函数代表一个中间件时。函数将会按照app.use的顺序调用函数去处理每个请求。函数调用主要传入以下3个参数:

app.use(function (req, res, next) {
  // req 是 Node.js http请求对象
  // res 是 Node.js http响应对象
  // next是一个调用下一个中间件的函数
})

除了plan函数,fn参数通常是Node.js HTTP服务器实例或者其他的Connect应用实例。

app.use(route, fn)

应用中使用函数,函数指的是中间件。对于URL(req.url属性)开头相同的每个请求,app.use都会调用相同的中间件函数。route可以有多个路径,而中间件函数有三个参数:

app.use('/foo', function (req, res, next) {
  // req 是 Node.js http请求对象
  // res 是 Node.js http响应对象
  // next是一个调用下一个中间件的函数
})

除了plan函数,fn参数通常是Node.js HTTP服务器实例或者其他的Connect应用实例。 route通常由路径分隔符/和.字符串组成。这意味着给定的/foo/和/foo是相同的,并且将会匹配/foo, /foo/, /foo/bar, and /foo.bar,但是不会匹配/foobar

route不区分大小写。

为了更加简单地创建中间件,一般写入route未知的路径,当fn被调用后,req.url将会被去除route部分(路径将会被赋值到req.originalUrl变量) 当例如,如果fn在/foo路由中使用, /foo/bar被请求时,req会生成url属性和originalUrl属性,此时,req.url === '/bar'req.originalUrl === '/foo/bar'