Koa
是一个基于nodejs
的入门级http
服务。 通过利用async
函数,更简单化、流程化、模块化的方式实现回调部分。
- 创建
koa2
工程
// 导入koa,和koa 1.x不同,在koa2中,我们导入的是一个class,因此用大写的Koa表示:
const Koa = require('koa');
// 创建一个Koa对象表示web app本身:
const app = new Koa();
// 对于任何请求,app将调用该异步函数处理请求:
app.use(async (ctx, next) => {
await next();
ctx.response.type = 'text/html';
ctx.response.body = '<h1>Hello, koa2!</h1>';
});
// 在端口3000监听:
app.listen(3000);
console.log('app started at port 3000...');
说明:
1. 参数ctx
是由koa
传入的封装了request
和response
的变量,next
是koa
传入的将要处理的下一个异步函数。
2. ctx
对象有一些简写的方法,例如ctx.url
相当于ctx.request.url
,ctx.type
相当于ctx.response.type
。
3. 由async
标记的函数称为异步函数,在异步函数中,可以用await
调用另一个异步函数。
4. koa
把很多async
函数组成一个处理链,每个async
函数都可以做一些自己的事情,然后用await next()
来调用下一个async
函数。我们把每个async
函数称为middleware
,这些middleware
可以组合起来,完成很多有用的功能。
koa middleware
app.use(async (ctx, next) => {
console.log(`${ctx.request.method} ${ctx.request.url}`); // 打印URL
await next(); // 调用下一个middleware
});
app.use(async (ctx, next) => {
const start = new Date().getTime(); // 当前时间
await next(); // 调用下一个middleware
const ms = new Date().getTime() - start; // 耗费时间
console.log(`Time: ${ms}ms`); // 打印耗费时间
});
app.use(async (ctx, next) => {
await next();
ctx.response.type = 'text/html';
ctx.response.body = '<h1>Hello, koa2!</h1>';
});
说明:
1. 调用app.use()
的顺序决定了middleware
的顺序
2. 如果一个middleware
没有调用await next()
,后续的middleware
将不再执行了,如检测权限时,ctx.response.status = 403
koa-router
-
router.get('/path', async fn)
:处理get
请求
const Koa = require('koa');
const app = new Koa();
const router = require('koa-router')();
app.use(async (ctx, next) => {
console.log(`Process ${ctx.method} ${ctx.url}...`);
await next();
});
router.get('/hello/:name', async (ctx, next) => {
let name = ctx.params.name;
ctx.response.body = `<h1>Hello, ${name}!</h1>`;
});
router.get('/', async (ctx, next) => {
ctx.response.body = '<h1>Index</h1>';
});
app.use(router.routes());
app.listen(3000, () => console.log('app started at port 3000...'));
-
router.post('/path', async fn)
:处理post
请求
post
请求通常会发送一个表单,或者JSON
,它作为request
的body
发送,需要引入koa-bodyparser
来解析原始request
请求,然后,把解析后的参数,绑定到ctx.request.body
中。
const Koa = require('koa');
const app = new Koa();
const router = require('koa-router')();
router.get('/', async (ctx, next) => {
ctx.response.body = `<h1>Index</h1>
<form action="/signin" method="post">
<p>Name: <input name="name" value="koa"></p>
<p>Password: <input name="password" type="password"></p>
<p><input type="submit" value="Submit"></p>
</form>`;
});
router.post('/signin', async (ctx, next) => {
let name = ctx.request.body.name || '';
let password = ctx.request.body.password || '';
console.log(`signin with name: ${name}, password: ${password}`);
if (name === 'koa' && password === '12345') {
ctx.response.body = `<h1>Welcome, ${name}!</h1>`;
} else {
ctx.response.body = `<h1>Login failed!</h1>
<p><a href="/">Try again</a></p>`;
}
});
// koa-bodyparser必须在router之前被注册到app对象上
const bodyParser = require('koa-bodyparser');
app.use(bodyParser());
app.use(router.routes());
app.listen(3000, () => console.log('app started at port 3000...'));
- 封装处理不同的
URL
url-koa/
|
+- controllers/
| |
| +- login.js <-- 处理login相关URL
| |
| +- users.js <-- 处理用户管理相关URL
|
+- app.js <-- 使用koa的js
|
+- package.json <-- 项目描述文件
|
+- node_modules/ <-- npm安装的所有依赖包
// app.js
const Koa = require('koa');
// 引入koa-bodyparser来解析原始request请求,然后,把解析后的参数,绑定到ctx.request.body中
const bodyParser = require('koa-bodyparser');
// 导入controller middleware:
const controller = require('./controller');
const app = new Koa();
app.use(bodyParser())
.use(controller())
.listen(3000, () => console.log('app started at port 3000...'));
// controller.js:扫描controllers目录和创建router的代码,作为一个简单的middleware使用
const fs = require('fs');
const addMapping = (router, mapping) => {
for (let url in mapping) {
if (url.startsWith('GET ')) {
// 如果url类似"GET xxx"
let path = url.substring(4);
router.get(path, mapping[url]);
console.log(`register URL mapping: GET ${path}`);
} else if (url.startsWith('POST ')) {
// 如果url类似"POST xxx"
let path = url.substring(5);
router.post(path, mapping[url]);
console.log(`register URL mapping: POST ${path}`);
} else {
// 无效的URL
console.log(`invalid URL: ${url}`);
}
}
}
const addControllers = (router, dir) => {
// 这里可以用sync是因为启动时只运行一次,不存在性能问题
let files = fs.readdirSync(__dirname + '/' + dir);
// 过滤出.js文件
let js_files = files.filter((f) => {
return f.endsWith('.js');
});
// 处理每个js文件
for (let f of js_files) {
console.log(`process controller: ${f}...`);
// 导入js文件
let mapping = require(__dirname + '/' + dir + '/' + f);
addMapping(router, mapping);
}
}
module.exports = function (dir) {
let controllers_dir = dir || 'controllers'; // 如果不传参数,扫描目录默认为'controllers'
let router = require('koa-router')();
addControllers(router, controllers_dir);
return router.routes();
};
// login.js
const fn_index = async (ctx, next) => {
ctx.response.body = `<h1>Index</h1>
<form action="/signin" method="post">
<p>Name: <input name="name" value="koa"></p>
<p>Password: <input name="password" type="password"></p>
<p><input type="submit" value="Submit"></p>
</form>`;
};
const fn_signin = async (ctx, next) => {
let name = ctx.request.body.name || '';
let password = ctx.request.body.password || '';
console.log(`signin with name: ${name}, password: ${password}`);
if (name === 'koa' && password === '12345') {
ctx.response.body = `<h1>Welcome, ${name}!</h1>`;
} else {
ctx.response.body = `<h1>Login failed!</h1>
<p><a href="/">Try again</a></p>`;
}
};
module.exports = {
'GET /': fn_index,
'POST /signin': fn_signin
};
// hello.js
const fs = require('fs');
const fn_hello = async (ctx, next) => {
let name = ctx.params.name;
ctx.response.body = `<h1>Hello, ${name}!</h1>`;
};
module.exports = {
'GET /hello/:name': fn_hello
};
-
context
koa
为了能够简化API
,引入上下文context
概念,将原始请求对象req
和响应对象res
封装并挂载到context
上,并且在context
上设置getter
和setter
,从而简化操作。 -
中间件
Koa
中间件机制就是函数组合的概念,将一组需要顺序执行的函数复合为一个函数,外层函数的参数实际是内层函数的返回值。洋葱圈模型。
网友评论