body-parser
处理post请求的数据
function bodyParser(ctx){
return async (ctx,next)=>{
await new Promise((resolve,reject)=>{
let arr = [];
ctx.req.on('data', function (chunk) {
arr.push(chunk);
});
ctx.req.on('end', function () {
if (ctx.get('Content-Type') === 'application/x-www-form-urlencoded'){
// a=1&b=2;
ctx.request.body = require("querystring").parse(Buffer.concat(arr).toString());
}
resolve();
});
});
await next();
}
}
复制代码
better-body
处理post请求并且可以处理文件。
function betterBody({uploadDir}) {
return async (ctx,next)=>{
await new Promise((resolve,reject)=>{
let arr = [];
ctx.req.on('data',function (chunk) {
arr.push(chunk);
})
ctx.req.on('end',function () {
let r = Buffer.concat(arr);
// 文件上传类型
let contentType = ctx.get('Content-Type');
let fileds = {};
if (contentType.startsWith('multipart/form-data')){
let boundary = '--'+contentType.split('=')[1]; // 分隔符
let lines = r.split(boundary).slice(1,-1);
lines.forEach((line)=>{
let [head,body] = line.split('\r\n\r\n');
let h = head.toString(); // 头部信息
if(h.includes('filename')){
// 文件 拿到文件内容后写入到真正的文件中
let content = line.slice(head.length + 4,-2);
// 需要一个文件名
let filename = `upload_${uuid.v1()}`;
fs.writeFileSync(path.resolve(__dirname, 'upload', filename), content);
fileds['file'] = filename;
}else{
// 普通的输入框
let [,key] = h.match(/name="(.+?)"/);// 取出头部key
let value = body.toString().slice(0,-2); // 把内容取出来
fileds[key] = value;
}
})
}
ctx.request.fields = fileds;
resolve();
})
});
await next();
}
}
复制代码
koa-router
get方法将path和cb放到数组中保存,routes方法会找到path相同的来调用compose方法,越界的时候执行下一个中间件。
class KoaRouter{
constructor() {
this.middles = [];
}
get(path,cb){
this.middles.push({
path,cb
})
}
compose(routers, ctx, next){
function dispatch(index) {
if(index == routers.length) return next();
let router = routers[index];
router(ctx,()=>dispatch(index+1));
}
dispatch(0)
}
routes(){
return async (ctx,next)=>{
// 请求路径
let pathname = ctx.path;
let routers = this.middles.filter(m=>m.path === pathname);
// routers 是过滤出来的路径相同的路由
// ctx 每次执行的时候 都需要上下文
// next当越界时 应该调用下一个中间件
routers = routers.map(router=>router.cb);
this.compose(routers, ctx, next);
}
}
}
module.exports = KoaRouter;
复制代码
ejs模板
正则匹配+字符串拼接+new Function()(将字符串转换为函数)
let fs = require('fs');
let path = require('path');
let templateStr = fs.readFileSync(path.join(__dirname, 'template.ejs'), 'utf8');
function render(templateStr,obj) {
// 我们拼接了一个大的字符串 并且用with来包裹 我需要让这个字符串执行
let head = `with(obj){\r\n`;
head += "var str = `";
let content = templateStr.replace(/<%=(.*?)%>/g,function () {
return "${"+arguments[1] +"}"
})
content = content.replace(/<%(.+?)%>/g,function () {
return '`\r\n' + arguments[1] + '\r\nstr+=`'
});
let tail = '`\r\n} \r\n return str;';
let total = head + content + tail;
let fn = new Function('obj',total);
return fn(obj);
}
复制代码
views
function views(dir) {
return async (ctx,next)=>{
ctx.render = function (p,obj) {
return new Promise((resolve,reject)=>{
let realPath = path.resolve(dir, p);
// 读取模板进行渲染
fs.readFile(realPath,'utf8',function (err,data) {
resolve(render(data, obj));
})
})
}
await next();
}
}
复制代码
koa-static
function static(dir) {
return async (ctx, next) => {
// 先找当前目录下是否有这个文件,如果没有向下执行
let p = path.join(dir, ctx.path);
try{
let statObj = await fs.stat(p);
if (statObj.isFile()){
ctx.set('Content-Type', `${mime.getType(p)};charset=utf8`);
ctx.body = fs.createReadStream(p);
}else{
p = path.join(p,'index.html');
await fs.access(p); // 判断有没有index.html 没有的话 会抛出异常
ctx.set('Content-Type', `text/html;charset=utf8`);
ctx.body = fs.createReadStream(p);
}
}catch(e){
await next();
}
}
}
复制代码