Simple application and implementation of express middleware
After seeing double the net Mu class teacher's class in conjunction with their understanding did some simple conclusion, if any impropriety, please correct me.
Mentioned express
would have to mention middleware, followed by a brief introduction about the expres
simple application middleware with some commonly used functions.
1. express simple application middleware
In the daily development project, login authentication is a very common scenario, this time express
middleware can come in handy. Next are using native node
and express
method middleware simple login authentication.
Scenario: before acquiring the blog list for login authentication, only can get a list of blog logged in.
Native node.js
realized login authentication:
if(method === 'GET' && req.path === '/api/blog/list') {
// 若req.session.username有值说明已经登录
if (req.session.username){
// 登录状态下可以获取博客列表
获取博客列表
}
}
express
Middleware login authentication:
function loginCheck(req, res, next) {
if (某个登录成功的条件) {
next(); // 登录成功执行next
} else {
res.json({
errno: -1,
msg: 'login fail'
})
}
}
// 若在登录状态下,loginCheck会执行next函数,从而执行第二个中间件,获取博客列表。
app.get('/api/blog/list', loginCheck, (req, res, next) => {
获得博客列表
})
While this simple example above seem original node.js
to make it easier, but the use of middleware package better way, in some parity complex projects can reflect the advantage.
2. express the use, get, post, next, listen
realization of the method
The above combined flowchart and complete code is given, a brief description of the process at the implementation:
Here with use
(and in the realization of the idea get
, post
it is the same) as an example:
Get the path and middleware
use
Incoming call when the user parameter toregister
the function,register
function parameters obtained after separating the two paths and an intermediate portion, stored in the pathinfo.path
, the middleware stored ininfo.stack
the. Thenregister
the function of the separating path and back to the intermediateuse
,use
after the intermediate path and get separated, which will be assigned toconstructor
the functionthis.routes.all
.
注意:register 在分离路径时需要注意的一点是对于没有路径只有中间件的参数会将其路径赋值为根路径
/
listen
方法的实现使用
express
时调用app.listen(3000)
实际上不只是监听3000
端口还创建了一个http
服务,参数是一个回调函数,代码如下:listen(...args) { const server = http.createServer(this.callback()); server.listen(...args); }
res.json
方法的实现在回调函数
callback
中首先实现一个res.json
方法,实现方式也比较简单。- 定义
res
的Header
的Content-type
为application/json
, - 执行
res.end({JSON.stringify(data)})
将用户传入的data
对象转换为JSON
字符串输出到屏幕上。
- 定义
获取需要执行的中间件
这部分通过
match
函数实现。通过req.url
和req.method.toLowerCase
来获取需要执行的中间件。中间实现的方法就有很多了,思路就是通过method
找出相应的路径和中间件,这个已经在this.routes
中分类存储了。然后找出路径和中间件之后利用indexOf
方法,if (url.indexOf(routeInfo.path) === 0)
如果if
判断成立说明满足条件,那么需要执行的中间件也就找出来了。app.next
方法的实现这部分通过
handle
函数实现。思路:定义一个next
函数,在那些需要执行的中间件中,先取出第一个,如果不为空那么开始执行,三个参数分别为req, res, next
。后面需要执行的中间件到底执不执行取决于前一个中间件的程序中是否调用了next
方法。代码如下:const next = () => { // shift 取得数组的第一项 const middleware = stack.shift(); if (middleware) { // 执行中间件函数 middleware(req, res, next); } }; // 定义完后立即执行 next();
注意:在定义完next函数之后,必须立即执行一次next函数,否则第一个需要执行的中间件也不会执行了。
Middleware passed in the next argument, here it is the next function definition, if a middleware program before executing the next function, it will then execute
stack.shift
out next middleware in the middleware needs to perform.
Attach the complete code:
const http = require('http');
const slice = Array.prototype.slice;
class LikeExpress {
constructor() {
// 存放路径和中间件,all中存放的是调用use传入的路径和中间件
this.routes = {
all: [],
get: [],
post: []
}
}
// 分离出路径和中间件,info.path中存放路径,info.stack 中存放中间件
register(path) {
const info = {};
if (typeof path === 'string') {
info.path = path;
// info.stack 存放所有的中间件
// 如果第一个参数是路由在取中间件时就要从数组的第2个位置开始取
// slice.call(arguments, 1) 的作用就是取arguments从第二个位置开始之后的所有元素都取出来并变成数组的形式。
info.stack = slice.call(arguments, 1);
} else {
// 如果第一个参数不是一个路由,那么我们就假定第一个参数是一个根路由
info.path = '/';
info.stack = slice.call(arguments, 0);
}
return info;
}
use() {
// 实际使用时,参数是通过use传递进来的
// 将所有的参数传入到register函数中
const info = this.register.apply(this, arguments);
// info 是一个对象,info.path 中存放的是路径,info.stack 中存放的是中间件
this.routes.all.push(info);
}
get() {
const info = this.register.apply(this, arguments);
this.routes.get.push(info);
}
post() {
const info = this.register.apply(this, arguments);
this.routes.post.push(info);
}
// 匹配use,get或post方法会执行的中间件
match(method, url) {
let stack = [];
if (url === '/favicon.ico') {
return stack;
}
// 获取routes
let curRoutes = [];
// concat 是数组中的一个方法,如果没有参数,那么会生成一个当前数组的副本并将其赋值给前面的变量,如果有参数会将参数加入到生成的副本的后面然后将其赋值给变量
// 如果是use,那么就把use中的路径和中间列表复制到curRoutes中
// 如果方法是get或post那么下面这句话,由于this.routes.all是undefined,所以会将当前curRoutes生成一个副本赋值给curRoutes,其实一点变化都没有
curRoutes = curRoutes.concat(this.routes.all);
// 如果是get或post,那么就把相应的路径和中间件复制到curRoutes中
curRoutes = curRoutes.concat(this.routes[method]);
curRoutes.forEach(routeInfo => {
// url='/api/get-cookie' routeInfo.path='/'
// url='/api/get-cookie' routeInfo.path='/api'
// url='api/get-cookie' routeInfo.path='/api/get-cookie'
if (url.indexOf(routeInfo.path) === 0) {
// 匹配成功
stack = stack.concat(routeInfo.stack);
}
});
return stack;
}
// 核心的 next 机制
handle(req, res, stack) {
const next = () => {
// 拿到第一个匹配的中间件
// shift 取得数组的第一项
const middleware = stack.shift();
if (middleware) {
// 执行中间件函数
middleware(req, res, next);
}
};
// 定义完后立即执行
next();
}
// callback是一个(req, res) => {} 的函数,结合http-test中的代码去理解
callback() {
return (req, res) => {
// res.json 是一个函数,在express中使用时传入一个对象即可在屏幕中显示出来,这里实现了这个功能
res.json = (data) => {
res.setHeader('Content-type', 'application/json');
res.end(
JSON.stringify(data)
);
};
const url = req.url;
const method = req.method.toLowerCase();
// 找到需要执行的中间件(通过路径和method,有可能一个需要执行的中间件也没有)
const resultList = this.match(method, url);
this.handle(req, res, resultList);
}
}
// express 中listen的作用不仅仅是监听端口,还要创建服务器
listen(...args) {
const server = http.createServer(this.callback());
server.listen(...args);
}
}
module.exports = () => {
return new LikeExpress()
};
Finish