这篇文章会讲些什么?
我们知道Koa类库主要有以下几个重要特性:
目标:完成基础可行新的Koa Server
核心代码如下:
class Koa {
private middleware: middlewareFn = () => {};
constructor() {}
listen(port: number, cb: noop) {
const server = http.createServer((req, res) => {
this.middleware(req, res);
});
return server.listen(port, cb);
}
use(middlewareFn: middlewareFn) {
this.middleware = middlewareFn;
return this;
}
}
const app = new Koa();
app.use((req, res) => {
res.writeHead(200);
res.end("A request come in");
});
app.listen(3000, () => {
console.log("Server listen on port 3000");
});
目标:接下来我们要完善listen和use方法,实现洋葱圈中间件模型
如下面代码所示,在这一步中我们希望app.use能够支持添加多个中间件,并且中间件是按照洋葱圈(类似深度递归调用)的方式顺序执行
app.use(async (req, res, next) => {
console.log("middleware 1 start");
// 具体原因我们会在下面代码实现详细讲解
await next();
console.log("middleware 1 end");
});
app.use(async (req, res, next) => {
console.log("middleware 2 start");
await next();
console.log("middleware 2 end");
});
app.use(async (req, res, next) => {
res.writeHead(200);
res.end("An request come in");
await next();
});
app.listen(3000, () => {
console.log("Server listen on port 3000");
});
上述Demo有三个需要我们注意的点:
我们会在接下来的代码中逐个分析这些使用方法的原因,下面我们来看一看具体怎么实现这种洋葱圈机制:
class Koa {
...
use(middlewareFn: middlewareFn) {
// 1、调用use时,使用数组存贮所有的middleware
this.middlewares.push(middlewareFn);
return this;
}
listen(port: number, cb: noop) {
// 2、 通过composeMiddleware将中间件数组转换为串行[洋葱圈]调用的函数,在createServer中回调函数中调用
// 所以真正的重点就是 composeMiddleware,如果做到的,我们接下来看该函数的实现
// BTW: 从这里可以看到 fn 是在listen函数被调用之后就生成了,这就意味着我们不能在运行时动态的添加middleware
const fn = composeMiddleware(this.middlewares);
const server = http.createServer(async (req, res) => {
await fn(req, res);
});
return server.listen(port, cb);
}
}
// 3、洋葱圈模型的核心:
// 入参:所有收集的中间件
// 返回:串行调用中间件数组的函数
function composeMiddleware(middlewares: middlewareFn[]) {
return (req: IncomingMessage, res: ServerResponse) => {
let start = -1;
// dispatch:触发第i个中间件执行
function dispatch(i: number) {
// 刚开始可能不理解这里为什么这么判断,可以看完整个函数在来思考这个问题
// 正常情况下每次调用前 start < i,调用完next() 应该 start === i
// 如果调用多次next(),第二次及以后调用因为之前已完成start === i赋值,所以会导致 start >= i
if (i <= start) {
return Promise.reject(new Error("next() call more than once!"));
}
if (i >= middlewares.length) {
return Promise.resolve();
}
start = i;
const middleware = middlewares[i];
// 重点来了!!!
// 取出第i个中间件执行,并将dispatch(i+1)作为next函数传给各下一个中间件
return middleware(req, res, () => {
return dispatch(i + 1);
});
}
return dispatch(0);
};
}
主要涉及到Promise几个知识点:
现在我们在回顾之前提出的几个问题:
koa中间件中为什么必须且只能调用一次next函数
可以看到如果不调用next,就不会触发dispatch(i+1),下一个中间件就没办法触发,造成假死状态最终请求超时
调用多次next则会导致下一个中间件执行多次
next() 调用为什么需要加 await
这也是洋葱圈调用机制的核心,当执行到 await next(),会执行next()【调用下一个中间件】等待返回结果,在接着向下执行
目标:封装Context,提供request、response的便捷操作方式
// 1、 定义KoaRequest、KoaResponse、KoaContext
interface KoaContext {
request?: KoaRequest;
response?: KoaResponse;
body: String | null;
}
const context: KoaContext = {
get body() {
return this.response!.body;
},
set body(body) {
this.response!.body = body;
}
};
function composeMiddleware(middlewares: middlewareFn[]) {
return (context: KoaContext) => {
let start = -1;
function dispatch(i: number) {
// ..省略其他代码..
// 2、所有的中间件接受context参数
middleware(context, () => {
return dispatch(i + 1);
});
}
return dispatch(0);
};
}
class Koa {
private context: KoaContext = Object.create(context);
listen(port: number, cb: noop) {
const fn = composeMiddleware(this.middlewares);
const server = http.createServer(async (req, res) => {
// 3、利用req、res创建context对象
// 这里需要注意:context是创建一个新的对象,而不是直接赋值给this.context
// 因为context适合请求相关联的,这里也保证了每一个请求都是一个新的context对象
const context = this.createContext(req, res);
await fn(context);
if (context.response && context.response.res) {
context.response.res.writeHead(200);
context.response.res.end(context.body);
}
});
return server.listen(port, cb);
}
// 4、创建context对象
createContext(req: IncomingMessage, res: ServerResponse): KoaContext {
// 为什么要使用Object.create而不是直接赋值?
// 原因同上需要保证每一次请求request、response、context都是全新的
const request = Object.create(this.request);
const response = Object.create(this.response);
const context = Object.create(this.context);
request.req = req;
response.res = res;
context.request = request;
context.response = response;
return context;
}
}
目标:支持通过 app.on("error"),监听错误事件处理异常
我们回忆下在Koa中如何处理异常,代码可能类似如下:
app.use(async (context, next) => {
console.log("middleware 2 start");
// throw new Error("出错了");
await next();
console.log("middleware 2 end");
});
// koa统一错误处理:监听error事件
app.on("error", (error, context) => {
console.error(`请求${context.url}发生了错误`);
});
从上面的代码可以看到核心在于:
下面我们看具体代码如何实现:
// 1、继承EventEmitter,增加事件触发、监听能力
class Koa extends EventEmitter {
listen(port: number, cb: noop) {
const fn = composeMiddleware(this.middlewares);
const server = http.createServer(async (req, res) => {
const context = this.createContext(req, res);
// 2、await调用fn,可以使用try catch捕获异常,触发异常事件
try {
await fn(context);
if (context.response && context.response.res) {
context.response.res.writeHead(200);
context.response.res.end(context.body);
}
} catch (error) {
console.error("Server Error");
// 3、触发error时提供context更多信息,方面日志记录,定位问题
this.emit("error", error, context);
}
});
return server.listen(port, cb);
}
}
至此我们已经使用TypeScript完成简版Koa类库,支持了
koa是一个基于node实现的一个新的web框架,它是由express框架的原班人马打造的。它的特点是优雅、简洁、表达力强、自由度高。它更express相比,它是一个更轻量的node框架
koa-easywechat注意:koa-easywechat中间件要写在最前面,也就是要第一个use,因为我在ctx上挂载了一个wechat对象,这个对象实现了大部分的微信接口,这样才能保证开发者在自己的写路由里,获取到ctx.wechat进行自己的业务开发
我们知道,Koa 中间件是以级联代码(Cascading) 的方式来执行的。类似于回形针的方式,今天这篇文章就来分析 Koa 的中间件是如何实现级联执行的。在 koa 中,要应用一个中间件,我们使用 app.use()
Koa 是一个新的 web 框架,由 Express 幕后的原班人马打造, 致力于成为 web 应用和 API 开发领域中的一个更小、更富有表现力、更健壮的基石。 通过利用 async 函数,Koa 帮你丢弃回调函数,并有力地增强错误处理。
安装 koa、简单使用、级联中间件的概念、获取get请求参数、获取post表单数据和文件上传、路由中间件 koa-router
想使用nodejs(koa)搭建一个完整的前后端,完成数据的增删改查,又不想使用数据库,那使用json文件吧。本文介绍了基于koa的json文件的增、删、改、查。
对于一个服务器应用来说,日志的记录是必不可少的,我们需要使用其记录项目程序每天都做了什么,什么时候发生过错误,发生过什么错误等等,便于日后回顾、实时掌握服务器的运行状态,还原问题场景
Koa 应用程序是一个包含一组中间件函数的对象,它是按照类似堆栈的方式组织和执行的。当一个中间件调用 next() 则该函数暂停并将控制传递给定义的下一个中间件。当在下游没有更多的中间件执行后,堆栈将展开并且每个中间件恢复执行其上游行为
上传文件在开发中是很常见的操作,今天我选择使用koa-multer中间件来实现这一功能,除了上传文件外,我还会对文件上传进行限制,以及发生上传错误时的处理。由于原来的 koa-multer 已经停止维护,我们要使用最新的 @koa/multer
Nodejs官方api支持的都是callback形式的异步编程模型。问题:callback嵌套问题,koa2 是由 Express原班人马打造的,是现在比较流行的基于Node.js平台的web开发框架,Koa 把 Express 中内置的 router、view 等功能都移除了
内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!