Koa源码解析

1.myKoa.js

  • 概述:Koa 是⼀个新的 web 框架, 致⼒于成为 web 应⽤和 API 开发领域中的⼀个更⼩、更富有 表现⼒、更健壮的基⽯。
  • koa是Express的下⼀代基于Node.js的web框架
  • koa2完全使⽤Promise并配合 async 来实现异步
const http = require("http")
const context = require('./context')
const request = require('./request')
const response = require('./response')

class myKoa {
    // 初始化中间件数组
    constructor() {
        this.middlewares = []
    }
    listen(...args) {
        const server = http.createServer(async (req, res) => {
            // this.callback(req,res)
            // 1.创建上下文
            const ctx = this.createContext(req, res)
            // 2.中间件合成
            const fn = this.compose(this.middlewares);
            // 3. 执行合成函数并传入上下文
            await fn(ctx)
            // 2.调用回调函数
            // this.callback(ctx)
            // 3.响应
            res.end(ctx.body)
        })
        server.listen(...args)
    }
    // use(callback) {
    //     this.callback = callback;
    // }
    use(middleware) {
        // 将中间件加到数组理
        this.middlewares.push(middleware)
    }
    /**
     * 异步中间件构成函数
     * @param {*} middlewares 
     */
    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)
                    })
                )
            }
        }
    }
    /**
     * 创建执行上下文
     * @param {*} req 
     * @param {*} res 
     */
    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
    }
}

module.exports = myKoa

2. request.js

module.exports = {
    get url() {
        return this.req.url
    },
    get method(){
        return this.req.method.toLowerCase()
    }
}

3.response.js

module.exports = {
    get body() {
        return this._body
    },
    set body(val) {
        this._body = val
    }
}

4.context.js

context

  • koa为了能够简化API,引⼊上下⽂context概念,将原始请求对象req和响应对象res封装并挂载到 context上,并且在context上设置getter和setter,从⽽简化操作。
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
    }
}

5.app.js

使用koa

// const http = require('http')

// const server = http.createServer((req,res)=>{
//     res.writeHead(200)
//     res.end('hi node')
// })

// server.listen(3000,()=>{
//     console.log('3000端口,监听成功')
// })

const myKoa = require('./myKoa')
const Router = require('./router')
const static = require('./static')


const app = new myKoa()
const router = new Router()

// app.use((req, res) => {
//     res.writeHead(200)
//     res.end('hi koa')
// })
router.get('/index', async ctx => {
    console.log('index,xx')
    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'; });

// app.use(ctx => {
//     ctx.body = 'hello myKoa'
// })
app.use(static(__dirname + '/public'));
app.use(router.routes())
app.use(require("./iptable"));
app.listen(3000, () => {
    console.log('3000端口,监听成功')
})

6.router.js

koa-router

class Router {
    constructor() {
        this.stack = [];
    }
    register(path, methods, middleware) {
        const route = { path, methods, middleware }
        this.stack.push(route)
    }
    // 实现get
    get(path, middleware) {
        this.register(path, 'get', middleware)
    }
    // 实现post
    post(path, middleware) {
        this.register(path, 'post', middleware)
    }
    routes() {
        const stock = this.stack
        return async function (ctx, next) {
            const currentPath = ctx.url
            let route;
            for (let i = 0, len = stock.length; i < len; i++) {
                const item = stock[i]
                if (currentPath === item.path && item.methods.indexOf(ctx.method) != -1) {
                    route = item.middleware
                    break
                }
            }
            if (typeof route === 'function') {
                route(ctx, next)
                return
            }
            // 执行下一个中间件
            await next()
        }
    }
}
module.exports = Router;

7.static.js

静态⽂件服务koa-static

  • 配置绝对资源⽬录地址,
  • 默认为static 获取⽂件或者⽬录信息
  • 静态⽂件读取
  • 返回
const fs = require("fs");
const path = require("path");
module.exports = (dirPath = "./public") => {
    return async (ctx, next) => {
        if (ctx.url.indexOf("/public") === 0) {
            // public开头 读取⽂件
            const url = path.resolve(__dirname, dirPath);
            const fileBaseName = path.basename(url);
            const filepath = url + ctx.url.replace("/public", "");
            console.log(filepath);
            // console.log(ctx.url,url, filepath, fileBaseName)
            try {
                stats = fs.statSync(filepath);
                if (stats.isDirectory()) {
                    const dir = fs.readdirSync(filepath);
                    // const
                    const ret = ['<div style="padding-left:20px">'];
                    dir.forEach(filename => {
                        console.log(filename);
                        // 简单认为不带⼩数点的格式,就是⽂件夹,实际应该⽤statSync
                        if (filename.indexOf(".") > -1) {
                            ret.push(
                                `<p><a style="color:black" href="${
                                ctx.url
                                }/${filename}">${filename}</a></p>`
                            );
                        } else {
                            // ⽂件
                            ret.push(
                                `<p><a href="${ctx.url}/${filename}">${filename}</a></p>`
                            );
                        }
                    });
                    ret.push("</div>");
                    ctx.body = ret.join("");
                } else {
                    console.log("⽂件");
                    const content = fs.readFileSync(filepath);
                    ctx.body = content;
                }
            } catch (e) {
                // 报错了 ⽂件不存在
                ctx.body = "404, not found";
            }
        } else {
            // 否则不是静态资源,直接去下⼀个中间件
            await next();
        }
    };
};

8.iptable.js

  • 请求拦截:⿊名单中存在的ip访问将被拒绝
module.exports = async function (ctx, next) {
    const { res, req } = ctx;
    const blackList = ['127.0.0.1'];
    const ip = getClientIP(req);

    if (blackList.includes(ip)) {//出现在⿊名单中将被拒绝
        ctx.body = "not allowed";
    } else {
        await next();
    }
};
function getClientIP(req) {
    return (
        req.headers["x-forwarded-for"] || // 判断是否有反向代理 IP
        req.connection.remoteAddress || // 判断 connection 的远程 IP
        req.socket.remoteAddress || // 判断后端的 socket 的 IP
        req.connection.socket.remoteAddress
    );
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值