大家好,我是有用就扩散,有用就点赞。
为什么需要Koa,对比HTTP
1)路由麻烦,if else过多(可以使用策略模式)
2)重复代码有点多,比如statusCode的赋值
3)请求解析与响应体包装,原始代码过于臃肿
4)请求的解析源代码太多,API不优雅
5)AOP的支持(面向切面编程),引入洋葱模型
洋葱圈模型设计有以下几点好处:
- 更好地封装和复用代码逻辑,每个中间件只需要关注自己的功能;
- 更清晰的程序逻辑,通过中间件的嵌套可以表明代码的执行顺序;
- 更好的错误处理,每个中间件可以选择捕获错误或将错误传递给外层;
- 更高的扩展性,可以很容易地在中间件栈中添加或删除中间件。
Koa源码初步实现
1.洋葱模型,use方法的组合
// koa.js
const http = require('http')
const context = require('./context')
const request = require('./request')
const response = require('./response')
class Koa {
constructor() {
this.middlewares = []
}
listen(...args) {
const server = http.createServer(async (req, res) => {
// 创建上下文
const ctx = this.createContext(req, res)
const fn = this.compose(this.middlewares)
await fn(ctx)
// 返回响应
res.end(ctx.body)
})
server.listen(...args)
}
use(middleware) {
this.middlewares.push(middleware)
}
// 通过getter和setter来拦截request和response,实现优雅设置body
createContext(req, res) {
const ctx = Object.create(context)
ctx.request = Object.create(request)
ctx.response = Object.create(response)
ctx.req = ctx.request.req = req
ctx.res = ctx.response.res = res
return ctx
}
// 实现koa的compose
compose(middlewares) {
return function (ctx) {
return dispatch(0)
function dispatch(i) {
const fn = middlewares[i]
if (!fn) return Promise.resolve()
return Promise.resolve(fn(ctx, function next() {
return dispatch(i + 1)
}))
}
}
}
}
module.exports = Koa
// context.js
module.exports = {
get url() {
return this.request.url
},
get body() {
return this.response.body
},
set body(val) {
this.response.body = val
},
get method() {
return this.request.method
}
}
// request.js
module.exports = {
get url() {
return this.req.url
},
get method() {
return this.req.method.toLowerCase()
}
}
// response.js
module.exports = {
get body() {
return this._body
},
set body(val) {
this._body = val
}
}
实现
// app.js
const Koa = require('./koa');
const app = new Koa();
// 洋葱模型,use方法的组合
const delay = () => Promise.resolve(resolve => setTimeout(() => resolve(), 2000));
app.use(async (ctx, next) => {
ctx.body = "1";
await next();
ctx.body += "5";
});
app.use(async (ctx, next) => {
ctx.body += "2";
await delay();
await next();
ctx.body += "4";
});
app.use(async (ctx, next) => {
ctx.body += "3";
});
app.listen(3000, () => {
console.log("listening on 3000");
})
效果
2.路由策略模式
// router.js
class Router {
constructor() {
this.stack = [];
}
register(path, methods, middleware) {
let route = {path, methods, middleware}
this.stack.push(route);
}
// 现在只支持get和post,其他的同理
get(path,middleware){
this.register(path, 'get', middleware);
}
post(path,middleware){
this.register(path, 'post', middleware);
}
routes() {
let stock = this.stack;
return async function(ctx, next) {
let currentPath = ctx.url;
let route;
for (let i = 0; i < stock.length; i++) {
let item = stock[i];
if (currentPath === item.path && item.methods.indexOf(ctx.method) >= 0) {
// 判断path和method
route = item.middleware;
break;
}
}
if (typeof route === 'function') {
route(ctx, next);
return;
}
await next();
};
}
}
module.exports = Router;
实现
// app.js
const Koa = require('./koa');
const app = new Koa();
const Router = require('./router')
const router = new Router()
router.get('/index', async ctx => { ctx.body = 'index page'; });
router.get('/post', async ctx => { ctx.body = 'post page'; });
router.get('/list', async ctx => { ctx.body = 'list page'; });
router.post('/index', async ctx => { ctx.body = 'post page'; });
// 路由实例输出父中间件 router.routes()
app.use(router.routes());
app.listen(3000, () => {
console.log("listening on 3000");
})
效果
3.组合的方法的几种实现方式
// middlewares.js
const add = (x, y) => x + y
const square = z => z * z
// 方法一 简单实现
// const fn = (x, y) => square(add(x, y))
// 方法二 简单组合封装
// const compose = (fn1, fn2) => (...args) => fn2(fn1(...args))
// const fn = compose(add, square)
// 方法三 实现多个方法组合
// const compose = (...[first,...other]) => (...args) => {
// let ret = first(...args)
// other.forEach(fn => {
// ret = fn(ret)
// })
// return ret
// }
// const fn = compose(add, square)
// 方法四 考虑中间件执行顺序,实现多个方法组合,同时考虑异步
function compose(middlewares) {
return function () {
return dispatch(0)
function dispatch(i) {
const fn = middlewares[i]
if (!fn) return Promise.resolve()
return Promise.resolve(fn(function next() {
return dispatch(i + 1)
}))
}
}
}
// 方法四实现例子
async function fn1(next) {
console.log('fn1')
await next()
console.log('end fn1')
}
async function fn2(next) {
console.log('fn2')
await delay()
await next()
console.log('end fn2')
}
function fn3(next) {
console.log('fn3')
}
function delay() {
return Promise.resolve(res => {
setTimeout(() => reslove(), 2000)
})
}
const middlewares = [fn1, fn2, fn3]
const finalFn = compose(middlewares)
finalFn()
nction fn3(next) {
console.log(‘fn3’)
}
function delay() {
return Promise.resolve(res => {
setTimeout(() => reslove(), 2000)
})
}
const middlewares = [fn1, fn2, fn3]
const finalFn = compose(middlewares)
finalFn()
[**源码**](https://github.com/XYuanZ/Web-study)
欢迎各位大哥投稿 PR
标签:Node,body,return,Koa,ctx,next,HTTP,function,const
From: https://blog.csdn.net/qq_44186810/article/details/140595159