【Node】Koa的简单实现

简单实现Koa.js

入口文件application.js

const http = require('http')
const { Stream } = require('stream')
const context = require('./context')
const request = require('./request')
const response = require('./response')


class Application {
    constructor() {
        this.middleware = [] // 保存用户添加的中间件函数

        this.context = Object.create(context)
        this.request = Object.create(request)
        this.response = Object.create(response)
    }

    listen(...args) {
        const server = http.createServer(this.callback())
        server.listen(...args)
    }

    use(fn) {
        this.middleware.push(fn)
    }

    // 异步递归遍历调用中间件处理函数,实现洋葱模型
    compose(middleware) {
        return function (context) {
            const dispatch = index => {
                if (index >= middleware.length) return Promise.resolve()
                const fn = middleware[index]
                return Promise.resolve(
                    fn(context, () => dispatch(index + 1))
                )
            }
            return dispatch(0)
        }
    }

    // 构造上下文对象
    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
    }
	
	// 服务器启动后的回调函数
    callback() {
        const fnMiddleware = this.compose(this.middleware)
        const handleRequest = (req, res) => {
            const context = this.createContext(req, res)
            fnMiddleware(context).then(() => {
                // 处理body的不同格式
                respond(context)
                // res.end(context.body)
            }).catch(err => {
                res.end(err.message)
            })
        }
        return handleRequest
    }
}

function respond(ctx) {
    const body = ctx.body
    const res = ctx.res

    if (body === null) {
        res.statusCode = 204
        return res.end()
    }
    if (typeof body === 'string') return res.end(body)
    if (Buffer.isBuffer(body)) return res.end(body)
    if (body instanceof Stream) return body.pipe(ctx.res)
    if (typeof body === 'number') return res.end(body + '')
    if (typeof body === 'object') {
        console.log(2)
        const jsonStr = JSON.stringify(body)
        return res.end(jsonStr)
    }
}

module.exports = Application

context.js(简单实现,不完整)

const context = {
    // get method () {
    //     return this.request.method
    // }
}

defineProperty('request', 'method')
defineProperty('request', 'url')
defineProperty('response', 'body')

// 这里将每个方法的get set封装成一个函数
function defineProperty (target, name) {
    Object.defineProperty(context, name, {
        get () {
            return this[target][name]
        },
        set (value) {
            this[target][name] = value
        }
    })
    // context.__defineGetter__(name, function () {
    //     return this[target][name]
    // })
}

module.exports = context

request.js(简单实现,不完整)

const url = require('url')

const request = {
  get method () {
    return this.req.method
  },

  get header () {
    return this.req.headers
  },

  get url () {
    return this.req.url
  },

  get path () {
    return url.parse(this.req.url).pathname
  },

  get query () {
    return url.parse(this.req.url, true).query
  }
}

module.exports = request

response.js(简单实现,不完整)

const response = {
    set status (value) {
        this.res.statusCode = value
    }
}

module.exports = response

直接使用

const Koa = require('./koa')

const app = new Koa()

const one = (ctx, next) => {
    console.log('>> one')
    ctx.body = 'My koa1'
    next()
    ctx.body = {name: 'xp'}
    console.log('<< one')
}

const two = (ctx, next) => {
    console.log('>> two')
    ctx.body = 'My koa2'
    next()
    console.log('<< two')
}

const three = (ctx, next) => {
    console.log('>> three')
    next()
    console.log('<< three')
}

app.use(one)
app.use(two)
app.use(three)

app.listen(3000, () => {
    console.log('Server is running at 8000')
})
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Whoopsina

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值