啥是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;
複製程式碼