github地址https://github.com/sommouns/my_koa
const http = require('http')
const context = require('./context')
const request = require('./request')
const response = require('./response')
class SKoa {
constructor() {
this.middlewares = []
}
// 核心目的就是开个http服务
listen (...args) {
const server = http.createServer(async (req, res) => {
// 创建上下文
let ctx = this.createContext(req, res)
// 合并中间件
const fn = this.compose(this.middlewares)
// 执行中间件的聚合物
await fn(ctx)
res.end(ctx.body)
})
server.listen(...args)
}
use (middleware) {
this.middlewares.push(middleware)
}
createContext (req, res) {
// 主要就是对request和response做一层封装,然后全部存到ctx上
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
}
compose (middlewares) {
return function (ctx) {
return dispatch(0)
function dispatch (i) {
let fn = middlewares[i]
if (!fn) {
return Promise.resolve()
}
return Promise.resolve(
fn(ctx, function next () {
return dispatch(i + 1)
})
)
}
}
}
}
module.exports = SKoa
// context.js
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
}
}
// request.js
module.exports = {
get url () {
return this.req.url
},
get method () {
return this.req.method.toLowerCase()
}
}
// response.js
module.exports = {
get body () {
return this._body
},
set body (val) {
this._body = val
}
}
然后附上一些常用的中间件的实现
Logger
module.exports = async (ctx, next) => {
const start = Date.now()
await next()
const end = Date.now()
console.log('request ' + ctx.url + ', using ' + parseInt(end - start) + 'ms')
}
Router
Router主要的思路就是返回一个routes方法,它可以返回一个中间件。这个中间件用途也很简单,就是去匹配路由,当method和url匹配上之后,去调用后面再传入的中间接就可以了,要注意异步的问题!
class Router {
constructor() {
this.stack = []
}
register (path, method, middleware) {
let route = { path, method, middleware }
this.stack.push(route)
}
get (path, middleware) {
this.register(path, 'get', middleware)
}
post (path, middleware) {
this.register(path, 'post', middleware)
}
routes () {
let stock = this.stack
return async (ctx, next) => {
let currentPath = ctx.url
let route
for (let i = 0; i < stock.length; i++) {
let item = stock[i]
if (currentPath === item.path && item.method.indexOf(ctx.method) >= 0) {
route = item.middleware
break
}
}
if (typeof route === 'function') {
await route(ctx, next)
return
}
await next()
}
}
}
module.exports = Router
Static
思路很简单,就是去读路径,然后文件夹和文件分开处理(这里简单起见,只是颜色不同),文件夹就可以直接html拼接输出,文件就直接去读即可
const fs = require('fs')
const path = require('path')
module.exports = (dirPath = './public') => {
return async (ctx, next) => {
if (ctx.url.startsWith('/public')) {
const url = path.resolve(__dirname, dirPath)
const fileBaseName = path.basename(url)
const filePath = url + ctx.url.replace('/public', '')
console.log(filePath)
try {
const stats = fs.statSync(filePath)
// const
if (stats.isDirectory()) {
const dir = fs.readdirSync(filePath)
const ret = ['<div style="padding-left:20px">']
dir.forEach(filename => {
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 {
const content = fs.readFileSync(filePath)
ctx.body = content
}
} catch (e) {
ctx.body = "404 not found"
}
} else {
await next()
}
}
}
网友评论