7.20 手写一个express中间件,以及koa2中间件

Express中间件实现

1、like-express.js文件

const http = require('http')
const slice = Array.prototype.slice

class LikeExpress {
	constructor () {
		// 存放中间件的列表
		this.routes = {
			all: [],
			get: [],
			post: []
		}
	}

	// 分析参数是不是路由
	register (path) {
		const info = {}
		if (typeof path === 'string') {
			info.path = path
			// 从第二个参数开始,转换为数组,存入 stack
			info.stack = slice.call(arguments, 1)
		} else {
			info.path = '/'
			// 从第一个参数开始,转换为数组,存入 stack
			info.stack = slice.call(arguments, 0)	
		}
		return info
	}

	use () {
		const info = this.register.apply(this, arguments)
		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)
	}

	match (method, url) {
		let stack = []
		if (url === '/favicon.ico') {
			return stack
		}

		// 获取 routes
		let curRoutes = []
		curRoutes = curRoutes.concat(this.routes.all)
		curRoutes = curRoutes.concat(this.routes[method])

		curRoutes.forEach(routeInfo => {
			if (url.indexOf(routeInfo.path) === 0) {
				stack = stack.concat(routeInfo.stack)
			}
		})
		return stack
	}

	// 核心的 next 机制
	handle (req, res, stack) {
		const next = () => {
			// 拿到第一个匹配的中间件
			const middleware = stack.shift()
			if (middleware) {
				// 执行中间件
				middleware(req, res, next)
			}
		}
		next()
	}

	callback () {
		return (req, res) => {
			res.json = (data) => {
				res.setHeader('Content-Type', 'application/json')
				res.end(
					JSON.stringify(data)
				)
			}
			const url = req.url
			const method = req.method.toLowerCase()

			const resultList = this.match(method, url)
			
			this.handle(req, res, resultList)
		}
	}

	listen (...args) {
		const server = http.createServer(this.callback())
		server.listen(...args)
	}
}

// 工厂函数
module.exports = () => {
	return new LikeExpress
}

2、test.js文件

const express = require('./like-express')

// 本地 http 请求的实例
const app = express()

app.use((req, res, next) => {
	console.log('请求开始...', req.method, req.url)
	next()
})

app.use((req, res, next) => {
	// 假设在处理 cookie
	console.log('处理 cookie ...')
	req.cookie = {
		userId: 'abc123'
	}
	next()
})

app.use('/api', (req, res, next) => {
	console.log('处理 /api路由')
	next()
})

app.get('/api', (req, res, next) => {
	console.log('get /api路由')
	next()
})

// 模拟登录验证
function loginCheck(req, res, next) {
	setTimeout(() => {
		console.log('模拟登录成功')
		next()
	})
}

app.get('/api/get-cookie', loginCheck, (req, res, next) => {
	console.log('get /api/get-cookie')
	res.json({
		errno: 0,
		data: req.cookie
	})
})

app.listen(8000, () => {
	console.log('server is running on port 8000')
})

Koa2中间件实现

1、like-koa2.js文件

const http = require('http')

// 组合中间件
function compose(middlewareList) {
	return function (ctx) {
		function dispatch(i) {
			const fn = middlewareList[i]
			try {
				return Promise.resolve(
					fn(ctx, dispatch.bind(null, i + 1))
				)
			} catch (err) {
				return Promise.reject(err)
			}
		}
		return dispatch(0)
	}
}

class LikeKoa2 {
	constructor () {
		this.middlewareList = []
	}

	use (fn) {
		this.middlewareList.push(fn)
		return this
	}

	createContext (req, res) {
		const ctx = {
			req,
			res
		}
		ctx.query = req.query
		return ctx
	}

	handleRequest (ctx, fn) {
		return fn(ctx)
	}

	callback () {
		const fn = compose(this.middlewareList)
		return (req, res) => {
			const ctx = this.createContext(req, res)
			return this.handleRequest(ctx, fn)
		}
	}

	listen (...args) {
		const server = http.createServer(this.callback())
		server.listen(...args)
	}
}

module.exports = LikeKoa2

2、test.js文件

const Koa = require('./like-koa2');
const app = new Koa();

// logger
app.use(async (ctx, next) => {
	await next()
	const rt = ctx['X-Response-Time'];
	console.log(`${ctx.req.method} ${ctx.req.url} - ${rt}`);
});

// x-response-time
app.use(async (ctx, next) => {
	const start = Date.now();
	await next();
	const ms = Date.now() - start;
	ctx['X-Response-Time'] = `${ms}ms`;
});

// response
app.use(async ctx => {
	ctx.res.setHeader('Content-Type', 'application')
	ctx.res.end('This is like koa2');
});

app.listen(8000)

注:大佬写的代码,起一篇博客特此记录,面试可能会遇到。所以反复回顾。

你可能感兴趣的:(Web前端)