# koa

# 简化版的KOA

参考源代码,抽取核心进行简化

koa


// 3个必需对象
const http = require('http')
const EventEmitter = require('events')
const Stream = require('stream')

// 常用状态码
const statuses = {
    "200": "OK",
    "201": "Created",
    "202": "Accepted",
    "300": "Multiple Choices",
    "301": "Moved Permanently",
    "302": "Found",
    "400": "Bad Request",
    "401": "Unauthorized",
    "402": "Payment Required",
    "403": "Forbidden",
    "404": "Not Found",
    "405": "Method Not Allowed",
    "500": "Internal Server Error",
    "501": "Not Implemented",
    "502": "Bad Gateway",
}


// 自定义 request 对象
const request = {
    response: null,
    req: null,
    res: null,
    ctx: null,
    app: null,
}

// 自定义 response 对象
const response = {
    request: null,
    req: null,
    res: null,
    ctx: null,
    app: null,
}

// 自定义 context 对象
const context = {
    request: null,
    response: null,
    req: null,
    res: null,
    app: null,

    // 中间件运行的错误,会在这里统一处理
    onerror(err) {
        this.app.emit('error', err, this)

        const res = this.res;
        if ('ENOENT' == err.code) {
            err.status = 404;
        }
        if ('number' != typeof err.status || !statuses[err.status]) {
            err.status = 500;
        }

        const code = statuses[err.status];
        const msg = err.expose ? err.message : code;
        this.status = err.status;
        res.end(msg);
    }
}

class Koa extends EventEmitter {

    // 声明成员变量
    constructor(){
        super()
        this.middleware = []
        this.context = Object.create(context);
        this.request = Object.create(request);
        this.response = Object.create(response);
        this.ctx = this.context;
    }

    // 生成回调函数,进行监听
    listen(...args) {
        const server = http.createServer(this.callback())
        server.listen(...args);
    }

    // 加入中间件
    use(fn){
        this.middleware.push(fn)
        return this;
    }

    // 生成回调函数
    callback() {
        // 生成中间件组合
        const fn = this.compose(this.middleware)

        // 建立错误处理
        this.on('error', this.onerror);

        // 生成回调函数
        // 在每次请求中,都建立一个新ctx
        // 并用来中间件组合来处理
        const handleRequest = (req, res) => {
            const ctx = this.createContext(req, res)
            return this.handleRequest(ctx, fn)
        }

        return handleRequest;
    }

    // 组合中间件
    compose(middleware){
        return function(context, next) {
            return dispatch(0)
            function dispatch(i) {
                let fn = middleware[i]
                if (i === middleware.length) fn = next;
                if (!fn) return Promise.reslove()
                try {
                    return Promise.resolve((fn(context, () => dispatch(i+1))))
                }catch(err) {
                    return Promise.reject(err)
                }
            }
        }
    }

    // 请求处理函数
    handleRequest(ctx, fnMiddleware) {
        const res = ctx.res;
        res.statusCode = 404;
        const onerror = err => ctx.onerror(err)
        const handleResponse = () => this.respond(ctx)
        fnMiddleware(ctx).then(handleResponse).catch(onerror)
    }

    // 初始化上下文对象
    createContext(req, res){
        const context = Object.create(this.context);
        const request = context.request = Object.create(this.request)
        const response = context.response = Object.create(this.response)
        context.app = request.app = response.app = this;
        context.req = request.req = response.req = req;
        context.res = request.res = response.res = res;
        request.ctx = response.ctx = context;
        request.response = response;
        response.request = request;
        context.originalUrl = request.originalUrl = req.url;
        context.state = {};
        return context;
    }

    // 错误处理函数
    onerror(err) {
        if ( !(err instanceof Error) ) {
            throw new TypeError('non-error thrown: ' + err)
        }
        // 404 不处理
        if (404 === err.status ||  err.expose ) return;

        // 静默模式也不处理
        if ( this.silent ) return;

        // 打印错误到控制台
        const msg = err.stack || err.toString();
        console.error()
        console.error(msg.replace(/^/gm, ' '))
        console.error();
    }

    // 最终结果响应函数
    respond(ctx){
        const res = ctx.res;
        const code = ctx.status;
        let body = ctx.body;

        if ('HEAD' === ctx.method) {
            return res.end();
        }

        if (null == body) {
            body = String(code)
            return res.end(body)
        }

        if (Buffer.isBuffer(body)) return res.end(body);
        if ('string' === typeof body) return res.end(body);
        if (body instanceof Stream) return body.pipe(res);

        body = JSON.stringify(body)

        res.end(body)
    }

}

# 测试


const app = new Koa()

const delayGet = function(value, ms=0) {
    return new Promise((reslove, reject) => {
        setTimeout(() => reslove(value), ms)
    })
}


app.use(async ( ctx, next ) => {
    ctx.body = 'hello'
    await next()
})

app.use( async (ctx) => {
    let word = await delayGet(' 22', 1000)
    ctx.body += word
})


app.listen(8090)

更新时间: 4/30/2020, 2:21:17 AM