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
);
}