express 中间件的简单应用与实现
express 中间件的简单应用与实现
看了慕课网双越老师的课之后结合自己的理解做了一些简单的总结,如有不恰当之处,欢迎指正。
提到 express 就不得不提到中间件,接下来就简单的介绍一下 expres 中间件的简单应用与部分常用函数的实现。
1. express 中间件的简单应用
在日常项目的开发中,登录验证是一个非常常见的场景,这个时候 express 中间件就可以派上用场了。接下来分别使用原生 node 和 express 中间件的方法实现简单的登录验证。
应用场景:在获取博客列表之前要进行登录验证,只有在登录状态下才可以获取博客列表。
原生 node.js 实现登录验证:
if(method === 'GET' && req.path === '/api/blog/list') {
// 若req.session.username有值说明已经登录
if (req.session.username){
// 登录状态下可以获取博客列表
获取博客列表
}
}
express 中间件实现登录验证:
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) => {
获得博客列表
})
虽然上面这个简单的例子中看上去似乎原生 node.js 要更简单一些,但是使用中间件的方式封装性更好,在一些比价复杂的项目中就能体现出优势了。
2. express 中 use, get, post, next, listen 等方法的实现

结合上面的流程图和最后给出的完整代码,简单说明一下实现的过程:
这里以 use (在实现思路上和 get, post 是一样的)为例说明:
获取路径和中间件
use将用户调用时传入的参数传给register函数,register函数将获取到参数后分离出路径和中间两部分,路径存放在info.path中,中间件存放在info.stack中。然后register函数将分离开的路径和中间件再返回给use,use拿到分离后的路径和中间件之后,会将其赋值给constructor函数中this.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函数,否则第一个需要执行的中间件也不会执行了。
中间件中传入的参数中的next,就是在这里定义的next函数,如果前一个中间件程序中执行了next函数,那么会再执行
stack.shift取出需要执行的中间件中的下一个中间件。
附上完整代码:
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()
};
完
express 中间件的简单应用与实现的更多相关文章
- Express中间件简单的实现原理
上一篇理解Express的使用之后, 再总结一篇Express中间件的简单实现原理. 我们知道Express中间件就是一个个的函数, 那么怎么让这些函数有序的执行呢? 那就需要我们调用 next 函数 ...
- express中间件原理 && 实现
一.什么是express中间件? 什么是express中间件呢? 我们肯定都听说过这个词,并且,如果你用过express,那么你就一定用过express中间件,如下: var express = re ...
- 1.express中间件的简介
express中间件的意思 1, 中间件是一个模块.在js中,模块意味着函数,所以中间件是一个函数.那么这个函数长什么样子? 这还要从中间件的功能说起,它拦截http 服务器提供的请求和响应对象,执行 ...
- Express中间件的原理及实现
在Node开发中免不了要使用框架,比如express.koa.koa2拿使用的最多的express来举例子开发中肯定会用到很多类似于下面的这种代码 var express = require('exp ...
- node的express中间件之session
虽然session与cookie是分开保存的.但是session中的数据经过加密处理后默认保存在一个cookie中.因此在使用session中间件之前必须使用cookieParser中间件. app. ...
- Express中间件原理详解
前言 Express和Koa是目前最主流的基于node的web开发框架,他们的开发者是同一班人马.貌似现在Koa更加流行,但是仍然有大量的项目在使用Express,所以我想通过这篇文章说说Expres ...
- Node.js连接Mysql,并把连接集成进Express中间件中
引言 在node.js连接mysql的过程,我们通常有两种连接方法,普通连接和连接池. 这两种方法较为常见,当我们使用express框架时还会选择使用中间express-myconnection,可以 ...
- vue+webpack+express中间件接口使用
环境:vue 2.9.3; webpack 目的:接口的调用 跨域方式: 1.express中间的使用 2.nginx代理 3.谷歌浏览器跨域设置 -------------------------- ...
- express 中间件
[express 中间件] 中间件(Middleware) 是一个函数,它可以访问请求对象(request object (req)), 响应对象(response object (res)), 和 ...
随机推荐
- 【Oracle】Oracle数据库基本指标查看
目录 1.查看表空间 2.查看用户 3.查看数据库内存 4.查看数据库版本 5.oracle归档情况 6.查看redo log日志位置 7.查看数据库的控制文件 8.查看RMAN的备份情况 9.FRA ...
- Mybaits学习总结
一.Mybatis介绍 邮箱:1727292697 MyBatis是一个支持普通SQL查询,存储过程和高级映射的优秀持久层框架.MyBatis消除了几乎所有的JDBC代码和参数的手工设置以及对结果集的 ...
- Golang 指针理解
目录 0x00 指针地址和指针类型 0x01 从指针获取指针指向的值 0x02 使用指针修改值 0x03 返回函数中局部变量 0x04 使用 new() 创建指针 0x05 flag包的指针技术 0x ...
- leetcode105 从前序与中序遍历序列构造二叉树
如何遍历一棵树 有两种通用的遍历树的策略: 宽度优先搜索(BFS) 我们按照高度顺序一层一层的访问整棵树,高层次的节点将会比低层次的节点先被访问到. 深度优先搜索(DFS) 在这个策略中,我们采用深度 ...
- /etc/security/limits.conf 详解与配置
目录 一. /etc/security/limits.conf 详解 /etc/security/limits.conf 配置解析 /etc/security/limits.d/ 目录 二. ulim ...
- 多进程使用同一log4j配置导致的日志丢失与覆盖问题
最近接手了一个流传很多手的魔性古早代码,追日志时发现有明显缺失.对log4j不熟,不过可以猜测日志出问题肯定和多进程使用同一个log4j配置有关.经多次排查,终于捋清了其中逻辑.本文对排查过程进行复盘 ...
- Kubernetes权威指南 第一章:Kubernetes入门
Kubernetes是什么 官网 https://kubernetes.io/ 中文版:https://kubernetes.io/zh/ Kubernetes是谷歌十几年大规模容器管理经验的成果 是 ...
- 从壹开始 [ Design Pattern ] 之三 ║ 工厂模式 与 小故事
编者按: 定义一个用于创建对象的接口,让子类决定实例化哪一个类.工厂方法使得一个类的实例化延迟到子类. 工厂模式,是迄今为止,使用最多,最广泛的设计模式之一,它的身影几乎出现在每一个框架和个人代码之中 ...
- 你真的了解foreach吗?
引言 有C#基础的,当问到循环有哪些,会毫不犹豫的说出的for.do while.foreach及while这几种,但是到具体实际开发中,我们遇到一些问题,比如:到底选择哪种?为什么选择这种?哪种好像 ...
- Nginx防盗链、访问控制、解析PHP相关配置及Nginx代理
6月11日任务 12.13 Nginx防盗链12.14 Nginx访问控制12.15 Nginx解析php相关配置12.16 Nginx代理 扩展502问题汇总 http://ask.apelearn ...