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)), 和 ...
随机推荐
- 无法优化的O(n!) 算法
旅行商问题: 有一位旅行商,他需要前往5个城市. 要前往这5个城市,同时要确保旅程最短. 对于每种顺序,他都计算总旅程,再挑选出旅程最短的路线.5个城市有120种不同的排列方式.因此,在涉及5个城市时 ...
- ubuntu 16.04源码编译和配置caffe详细教程 | Install and Configure Caffe on ubuntu 16.04
本文首发于个人博客https://kezunlin.me/post/b90033a9/,欢迎阅读! Install and Configure Caffe on ubuntu 16.04 Series ...
- Java中的compareTo()方法,compareToIgnoreCase()方法
1.compareTo(String)方法: Java中String类有一个compareTo方法,该方法返回一个int类型的数据.其比较规则是:拿出字符串的第一个字符与参数的第一个字符进行比较,如果 ...
- SpringBoot 整合 Zookeeper 接入Starring微服务平台
背景 最近接的一个项目是基于公司产品Starring做的微服务支付平台,纯后台项目,实现三方支付公司和银行接口来完成用户账户扣款,整合成通用支付接口发布给前端调用. 但是扯蛋了,这边前端什么都不想做, ...
- centos6.7下安装glibc-2.17
glibc 所有版本下载地址 : http://ftp.gnu.org/pub/gnu/glibc/ 安装先决条件: #yum install gcc libffi-devel python-dev ...
- Knative Serverless 之道:如何 0 运维、低成本实现应用托管?
作者 | 牛秋霖(冬岛) 阿里云容器平台技术专家 关注"阿里巴巴云原生"公众号,回复关键词"1205"即可观看 Knative-Demo 演示视频. 导读:S ...
- 【合集】python 的一些妙用,推导式、三元表达式、with as 等
自己常用的内置函数 函数如下: dir len str list tuple zip map reduce(现在并入了functools中) 常用的进制转换 Oct hex bin lambda 表达 ...
- WebGPU学习(三):MSAA
大家好,本文学习MSAA以及在WebGPU中的实现. 上一篇博文 WebGPU学习(二): 学习"绘制一个三角形"示例 下一篇博文 WebGPU学习(四):Alpha To Cov ...
- springboot+apache前后端分离部署https
目录 1. 引言 2. 了解https.证书.openssl及keytool 2.1 https 2.1.1 什么是https 2.1.2 https解决什么问题 2.2 证书 2.2.1 证书内容 ...
- 白话OAuth2用户认证及鉴权标准流程
一.OAuth2需求场景 在说明OAuth2需求及使用场景之前,需要先介绍一下OAuth2授权流程中的各种角色: 资源拥有者(User) - 指应用的用户 认证服务器 (Authorization S ...