啥是koa?那个美女好像也叫koa
Koa2是现在最流行的基于Node.js平台的web开发框架;Koa 应用程序是一个包含一组中间件函数的对象,它是按照类似堆栈的方式组织和执行的。
const Koa = require('koa');
const app = new Koa();
app.use(async ctx => {
ctx.body = 'Hello World';
});
app.on('error', err => {
log.error('server error', err)
});
app.listen(3000);
复制代码
koa中间件以更传统的方式级联;那和express的中间件有什么区别呢?
- 区别只有一点express的中间件中存在异步操作时不会等待;而koa会等待异步操作完成后;才会执行下一个中间件;
express和koa的区别
let express = require('express');
let app = express();
app.use(async (req, res, next) => {
console.log(1);
await next();
console.log(2);
});
app.use(async (req, res, next) => {
console.log(3);
await new Promise((resolve, reject) => {
setTimeout(() => {
console.log('异步');
resolve()
}, 100);
})
await next();
console.log(4);
});
app.listen(3000);
// 1 3 2 异步 4
//遇到异步不会等待 直接执行;打乱原来的洋葱模型
复制代码
- koa
let Koa = require('Koa');
let app = new Koa();
app.use( async (ctx, next) => {
console.log(1);
await next();
console.log(2)
});
app.use(async (ctx, next) => {
console.log(3);
await new Promise((resolve, reject) => {
setTimeout(() => {
console.log('异步');
resolve()
}, 100);
})
await next();
console.log(4)
})
app.listen(3000);
// 1 3 异步 4 2
复制代码
koa使用中间件获取请求体
const bodyParser = require('koa-bodyparser');
app.use(bodyParser());
app.use(async (ctx)=>{
let body=ctx.request.body;//得到请求体
})
复制代码
使用中间件获取包含文件的请求体
const bodyParser = require('koa-better-body');
et convert = require('koa-convert'); // 将1.0的中间件 转化成2.0中间件
app.use(convert(bodyParser({
uploadDir: path.join(__dirname, 'uploads'),
keepExtensions: true
})));
app.use(async (ctx)=>{
let body=ctx.request.fields;//得到文件信息
})
复制代码
路由中间件
//一级路由
const Router = require('koa-router');
let user = new Router();
user.get('/user', function (ctx) {
ctx.body = 'get user ';
}).get('/query/:id', function (ctx) {
ctx.body = ctx.params;
}).post('/user', function (ctx) {
ctx.body = 'post user ';
}).get('/home', function (ctx) {
ctx.body = 'get home ';
});
app.use(user.routes());
//多级路由
let user = new Router();
user.get('/add', function (ctx) {
ctx.body = 'get user add ';
});
let article = new Router();
article.get('/add', function (ctx) {
ctx.body = 'get article add ';
});
let router = new Router();
router.use('/user', user.routes()); // /user/add
router.use('/article', article.routes()); ///article/add
app.use(router.routes());
复制代码
koa自带存取cookie方法
//ctx.cookies.get(name,[optins]):读取上下文请求中的cookie。
//ctx.cookies.set(name,value,[options]):在上下文中写入cookie。
app.keys = ['hello'];
ctx.cookies.set('name','sss',{
domain:'localhost', // 在哪个域名下设置cookie
path:'/',// 在哪个路径下设置cookie
maxAge:10*1000, // 最大存活时间
httpOnly:false, //是否只用http请求中获得
overwrite:true,//是否允许重写
signed:true//签名要app.keys
});
expires:cookie失效时间
复制代码
session
let Koa = require('koa');
let app = new Koa();
let Router = require('koa-router');
const session = require('koa-session');
app.keys = ['key']
app.use(session({},app)); // 用了这个中间件 可以在ctx上增加session属性
router.get('/cross', (ctx,next)=> {
let n = ctx.session.n || 0;
ctx.session.n = ++n;
ctx.body = ctx.session.n;
});
app.use(router.routes());
app.use(router.allowedMethods()); // 405
复制代码
模板引擎
let Koa = require('koa');
let app = new Koa();
let Router = require('koa-router');
let router = new Router();
let views = require('koa-views');
app.use(views(__dirname, {
map:{'html':'ejs'}//识别HTML
}));
router.get('/',async (ctx,next)=>{
// 如果不写return 这个函数执行完就结束了 模板还没有被渲染,ctx.body = ''
// 如果使用return会等待这个返回的promise执行完后才把当前的promise完成
return ctx.render('ejs.html',{title:'标题'});
})
app.use(router.routes());
app.listen(3000);
复制代码
静态资源中间件
const static = require('koa-static')
app.use(static(path.join( __dirname, 'public')))
app.use( async ( ctx ) => {
ctx.body = 'Not Found'
})
复制代码
自己实现koa
let EventEmitter = require('events');
let http = require('http');
let context = require('./context');
let request = require('./request');
let response = require('./response');
class Koa extends EventEmitter{
constructor(){
super();
this.middlewares = [];
this.context = context;
this.request = request;
this.response = response;
}
use(fn){
this.middlewares.push(fn);
}
createContext(req,res){
let ctx = Object.create(this.context);
let request = Object.create(this.request);
let response = Object.create(this.response);
ctx.request = request;
ctx.response = response;
ctx.req = ctx.request.req = req;
ctx.res = ctx.response.res = res;
return ctx;
}
compose(middlewares,ctx){
// express
function dispatch(index) {
if (index === middlewares.length) return Promise.resolve();
let route = middlewares[index];
// 把每个中间件都包装成promise
return Promise.resolve(route(ctx, () => dispatch(index+1)));
}
return dispatch(0);
}
handleRequest(req,res){
let ctx = this.createContext(req,res);
res.statusCode = 404;
// this.fn(context); 需要把中间件函数组合起来
let fn = this.compose(this.middlewares,ctx);
let Stream = require('stream');
fn.then(data=>{
if(typeof ctx.body == 'object'){
res.setHeader('Content-Type', 'application/json;charset=utf8');
res.end(JSON.stringify(ctx.body))
} else if (ctx.body instanceof Stream){
ctx.body.pipe(res);
}
else if (typeof ctx.body === 'string' || Buffer.isBuffer(ctx.body)) {
res.setHeader('Content-Type', 'text/html;charset=utf8');
res.end(ctx.body);
} else {
res.end('Not found')
}
}).catch(err=>{
this.emit('error',err);//
res.statusCode = 500;
res.end(`server error`);
})
}
listen(){
let server = http.createServer(this.handleRequest.bind(this));
server.listen(...arguments);
}
}
module.exports = Koa;
//ctx
let proto = {}
// 代理的作用
function defineGetter(property,name) {
proto.__defineGetter__(name,function () {
return this[property][name];
})
}
function defineSetter(property,name) {
// ctx.body = xxx
// ctx.response.body = xxx
proto.__defineSetter__(name,function (val) {
this[property][name] = val;
})
}
// ctx.query = ctx.request.query
defineGetter('request','query');
defineGetter('response','body');
defineSetter('response','body');
module.exports = proto;
//request
let request = { // 封装求的方法
get url(){
return this.req.url
},
get path(){
let {pathname:path} = require('url').parse(this.req.url);
return path;
},
get query(){
let { query } = require('url').parse(this.req.url,true);
return query;
}
// .........
}
module.exports = request;
//response
let response = {
get body(){
return this._body;
},
set body(value){
this.res.statusCode = 200;
this._body = value;
}
}
module.exports = response;
复制代码