在学习koa2之前先简单屡一下express、koa、koa2的关系;众所周知,这三者都是TJ大神的产物,那他们到底有什么不同呢?
express的异步操作是基于es5的回调函数嵌套,koa的异步操作是基于es6的generator函数来实现的,而koa2则是基于es7的async,await来实现的;
web框架 | 语法 | 备注 |
---|---|---|
express | es5 | 回调嵌套 |
koa | es6 | Generator函数+yield语句+Promise |
koa2 | es7 | async/await+Promise |
基本用法
const Koa = require('koa')
const app = new Koa() // 创建了一个server实例
const main = ctx => {
/*
Koa 提供一个 Context 对象,表示一次对话的上下文(包括 HTTP 请求和 HTTP 回复)。通过加工这个对象,就可以控制返回给用户的内容。
*/
ctx.response.body = 'hello world'
}
// app.use((ctx) => {
// ctx.response.body = 'hello world'
// })
app.use(main) //使用app.use方法加载main函数。
app.listen(3300) // 监听端口
HTTP Response 的类型
const Koa = require('koa')
const app = new Koa()
const main = ctx => {
/* ctx.request.accepts()方法如果没有提供类型,则返回 所有 可接受的类型
形如:[ 'text/html',
'application/xhtml+xml',
'image/webp',
'image/apng',
'application/xml'
]
若没有找到匹配项则返回false;
如果接收到任何类型的接收头,则会返回第一个类型。 因此,提供的类型的顺序很重要
*/
if (ctx.request.accepts('xml')) { // 这里xml判断在先,所以会被先匹配到,所以返回'<data>Hello World</data>'
ctx.response.type = 'xml';
ctx.response.body = '<data>Hello World</data>';
} else if (ctx.request.accepts('json')) {
ctx.response.type = 'json';
ctx.response.body = { data: 'Hello World' };
} else if (ctx.request.accepts('html')) {
ctx.response.type = 'html';
ctx.response.body = '<p>Hello World</p>';
} else {
ctx.response.type = 'text';
ctx.response.body = 'Hello World';
}
};
app.use(main)
app.listen(3300)
网页模板
const Koa = require('koa')
const app = new Koa()
const fs = require('fs')
// const fs = require('fs.promised')
// 这里把fs.readFile封装成了一个promise对象
let readFilePromise = function(url) {
return new Promise((resolve,reject) => {
fs.readFile(url, (err, data) => {
if (err) {
reject(err);
}
resolve(data);
})
})
}
const main = async ctx => {
console.log(123);
ctx.response.type = 'html';
// 这里会等待 await 后的异步任务完成后,再继续往下执行
let data = await readFilePromise('./1.html').then((data) => {
return data;
})
ctx.response.body = data;
};
app.use(main)
app.listen(3300)
koa-route 模块
const Koa = require('koa')
const app = new Koa()
const route = require('koa-route')
const about = ctx => {
ctx.response.type = 'html'
ctx.response.body = '<a href="/">Index Page</a>'
}
const main = ctx => {
ctx.response.body = 'Hello World'
}
app.use(route.get('/',main)) // 当请求路径为'/'时调用的时main处理函数
app.use(route.get('/about',about)) // 当请求路径为'/about'时调用的时about处理函数
app.listen(3300)
静态资源
const Koa = require('koa')
const app = new Koa()
const route = require('koa-route')
const server = require('koa-static') // 加载静态资源的模块
const path = require('path')
const fs = require('fs')
let readFilePromise = function(url) {
return new Promise((resolve, reject) => {
fs.readFile(url, (err, data) => {
if (err) {
reject(err);
}
resolve(data);
})
})
}
const about = ctx => {
ctx.response.type = 'html'
ctx.response.body = '<a href="/">Index Page</a>'
}
const main = async ctx => {
ctx.response.type = 'html';
let data = await readFilePromise('./1.html').then((data) => { // 实际可省略,详看异步中间件
return data;
})
ctx.response.body = data;
};
const redirect = ctx => { // 重定向,访问'/redirect'会被重定向到根路径,返回的StatusCode为302
ctx.response.redirect('/');
};
app.use(server(path.join(__dirname))) // 加载静态资源
app.use(route.get('/', main))
app.use(route.get('/about', about))
app.use(route.get('/redirect', redirect))
app.listen(3300)
中间件与中间件栈
const Koa = require('koa');
const app = new Koa();
const one = (ctx, next) => {
console.log('>> one');
next();
console.log('<< one');
}
const two = (ctx, next) => {
console.log('>> two');
next();
console.log('<< two');
}
const three = (ctx, next) => {
console.log('>> three');
next();
console.log('<< three');
}
// 最外层的执行顺序由app.use的顺序决定(下面这三个家伙的顺序)
app.use(one);
app.use(two);
app.use(three);
/*
>> one
>> two
>> three
<< three
<< two
<< one
*/
app.listen(3300);
- 最外层的中间件首先执行。
- 调用next函数,把执行权交给下一个中间件。
- …
- 最内层的中间件最后执行。
- 执行结束后,把执行权交回上一层的中间件。
- …
- 最外层的中间件收回执行权之后,执行next函数后面的代码。
异步中间件
const fs = require('fs');
const Koa = require('koa');
const app = new Koa();
let readFilePromise = function(url) {
return new Promise((resolve,reject) => {
fs.readFile(url, (err, data) => {
if (err) {
reject(err);
}
resolve(data);
})
})
}
const main = async function (ctx, next) {
ctx.response.type = 'html';
let data = await readFilePromise('./1.html') // await的作用其实就是替代了then方法,将resolve的值直接返回,使用起来更加方便。
ctx.response.body = data
};
app.use(main);
app.listen(3300);
中间件的合成
const Koa = require('koa')
const app = new Koa()
const compose = require('koa-compose') // koa-compose模块可以将多个中间件合成为一个,我的理解作用是可以不用写多个app.use了
const router = require('koa-route')
const fs = require('fs.promised')
const server = require('koa-static')
const path = require('path')
/*为了方便处理错误,最好使用try...catch将其捕获。但是,为每个中间件都写try...catch太麻烦,
我们可以让***最外层***的中间件,负责所有中间件的错误处理。*/
const handle = async (ctx, next) => {
try {
await next()
} catch(err) {
ctx.response.status = err.statusCode || err.status || 500
ctx.response.body = {
message: err.message
}
}
}
const logger = async (ctx, next) => {
console.log(`${Date.now()} ${ctx.request.method} ${ctx.request.url}`);
await next();
}
const main = async ctx => {
let data = await fs.readFile('./1324.html', 'utf8')
// ctx.throw(500) // 这条语句可以使程序抛出错误被最外层的handle函数捕捉到(原因请参考中间件与中间件栈里的说明)
ctx.response.type = 'html';
ctx.response.body = data
};
const middlewares = compose([handle, logger, main]); // 把多个中间件整合到一起(少些app.use)
app.use(server(path.join(__dirname)))
app.use(router.get('/',middlewares)) // 用路由处理对应的中间件
// app.use(middlewares);
app.listen(3300)
表单
const Koa = require('koa')
const koaBody = require('koa-body') // koa-body模块可以用来从 POST 请求的数据体里面提取键值对
const app = new Koa()
const main = async function(ctx) {
const body = ctx.request.body;
if (!body.name) ctx.throw(400, '.name required');
ctx.body = { name: body.name };
};
app.use(koaBody());
app.use(main);
app.listen(3300);
$ curl -X POST --data “name=Jack” 127.0.0.1:3300
{“name”:“Jack”}
$ curl -X POST --data “name” 127.0.0.1:3300
name required
文件上传
const Koa = require('koa');
const koaBody = require('koa-body');
const app = new Koa();
const path = require('path');
const fs = require('fs');
const main = async (ctx) => {
const file = ctx.request.body.files.upload || {}
// ctx.request.body.files.upload 为文件对象
const filePath = path.join(__dirname, file.name) // 保存文件的路径,当前路径加上文件名
const reader = fs.createReadStream(file.path) // 把上传的文件读出来,赋值给reader成为一个可读的对象
const writer = fs.createWriteStream(filePath) // 根据路径生成一个可写的writer对象
reader.pipe(writer) // 正式执行写入操作
ctx.response.body = 'uploadSuccess!'
};
app.use(koaBody({ multipart: true }));
app.use(main);
app.listen(3300);
参考资料http://www.ruanyifeng.com/blog/2017/08/koa.html
参考资料https://koa.bootcss.com/#application
喜欢博主的可以点赞关注一下
--------------------------------------------------------------- END ------------------------------------------------------------------