koa 2 快速入门教程
Koa
是一个新的基于 node
平台的 web
框架,由 Express
幕后的原班人马打造, 致力于成为 web
应用和 API
开发领域中的一个更小、更富有表现力、更健壮的基石。 通过利用 async
函数, Koa
帮你丢弃回调函数,并有力地增强错误处理。 Koa
并没有捆绑任何中间件, 而是提供了一套优雅的方法,帮助您快速而愉快地编写服务端应用程序。
因为 node.js
从 v7.6.0
开始完全支持 async/await
,不需要加 flag
,所以 node.js
环境都要 7.6.0
以上
建议:直接安装 node.js 8+
: node.js
官网地址 https://nodejs.org
安装 koa
$ npm i koa
helloword
第一步: 创建项目目录和安装项目依赖
$ mkdir koademo
$ cd koademo
$ npm init -y
$ npm i -P koa@2.7.0
第二步: 创建 app.js
文件,并添加代码如下:
// 引入 koa 框架
const Koa = require('koa');
// 创建 koa 的 app 实例,全局的应用程序是一个包含一组中间件函数的对象。它提供了注册中间件,缓存清理,代理支持和重定向等常见任务的方法
const app = new Koa();
// 注册一个中间件
app.use(async ctx => {
ctx.body = 'Hello World , from aicoder.com'; // 设置最终的响应的内容
});
// 设置开启监听并设置监听端口为 3006
app.listen(3006);
第三步: 启动运行
$ node app.js
第四步:打开浏览器测试
浏览器输入地址: http://localhost:3006/
如果输出:
'Hello World , from aicoder.com'
补充 async 与 await
async 可以修饰函数声明、匿名函数、函数表达式、自执行函数、类等。用 async 修饰的函数返回值都是 promise,如果返回值不是 promise 对象会被包裹成 Promise.resolve()。
await 后面可以跟一个 promise 对象,会等待 promise 状态发送改变后拿到 promise 的值, await 只能放到 async 函数中。
const demo = async function Demo() {
return 1;
}
async function Add() {
var t = await demo(); // 会拿到 t, t = 1;
}
(async fuction() {
await Add();
})();
关于 app
app
是 koa 的核心对象,提供了注册中间件,监听 http 等相关功能。
app.listen(...)
Koa 通过 listen 方法启动 HTTP 服务器监听,开启 HTT 服务。
const Koa = require('koa');
const app = new Koa();
app.listen(3006);
app.use(function)
将给定的中间件方法添加到此应用程序。中间件函数会收到两个参数 ctx
和 next
,两个参数的详细在下面。
app.keys=
设置签名的 Cookie
密钥。
这些被传递给 KeyGrip,但是你也可以传递你自己的 KeyGrip 实例。
例如,以下是可以接受的:
app.keys = ['im a newer secret', 'i like turtle'];
app.keys = new KeyGrip(['im a newer secret', 'i like turtle'], 'sha256');
这些密钥可以倒换,并在使用 { signed: true } 参数签名 Cookie 时使用。
ctx.cookies.set('name', 'tobi', { signed: true });
app.context
app.context 是从其创建 ctx 的原型。您可以通过编辑 app.context 为 ctx 添加其他属性。这对于将 ctx 添加到整个应用程序中使用的属性或方法非常有用,这可能会更加有效(不需要中间件)和/或 更简单(更少的 require()),而更多地依赖于 ctx,这可以被认为是一种反模式。
例如,要从 ctx 添加对数据库的引用:
app.context.db = db();
app.use(async ctx => {
console.log(ctx.db);
});
注意: ctx 上的许多属性都是使用 getter ,setter 和 Object.defineProperty() 定义的。你只能通过在 app.context 上使用 Object.defineProperty() 来编辑这些属性(不推荐)。
错误处理
默认情况下,将所有错误输出到 stderr,除非 app.silent 为 true。 当 err.status 是 404 或 err.expose 是 true 时默认错误处理程序也不会输出错误。 要执行自定义错误处理逻辑,如集中式日志记录,您可以添加一个 “error” 事件侦听器:
app.on('error', err => {
log.error('server error', err)
});
如果 req/res 期间出现错误,并且 无法 响应客户端,Context 实例仍然被传递:
app.on('error', (err, ctx) => {
log.error('server error', err, ctx)
});
当发生错误 并且 仍然可以响应客户端时,也没有数据被写入 socket 中,Koa 将用一个 500 “内部服务器错误” 进行适当的响应。在任一情况下,为了记录目的,都会发出应用级 “错误”。
context 上下文
Koa
提供一个 Context
对象,表示一次对话的上下文(包括 HTTP 请求 req 和响应 res)。通过加工这个对象,获取请求的内容,还可以控制返回给用户的内容。
// 引入 koa 框架
const Koa = require('koa');
// 创建 koa 的 app 实例,全局的应用程序是一个包含一组中间件函数的对象。它提供了注册中间件,缓存清理,代理支持和重定向等常见任务的方法
const app = new Koa();
// 注册一个中间件
app.use(async ctx => {
ctx.body = 'Hello World , from aicoder.com'; // 设置最终的响应的内容
});
例如在上面这个例子中, ctx
被传入到 app.use(fn)
的中间件函数中。
我们可以获取请求的内容和响应的内容:
app.use(async ctx => {
// ctx.body = 'Hello World , from aicoder.com'; // 设置最终的响应的内容
ctx.response.body = 'hi, aicoder.com'; // 设置响应内容
console.log(ctx.request.url); // 获取请求的 url 地址。
});
ctx.request
koa 的 Request 对象.见下文。
ctx.response
koa 的 Response 对象.见下文。
ctx.state
推荐的命名空间,用于通过中间件传递信息和你的前端视图。
ctx.state.user = await User.find(id);
ctx.app
应用程序实例引用。
cookie 操作
ctx.cookies.get(name, [options])
通过 options 获取 cookie name:
ctx.cookies.set(name, value, [options])
通过 options 设置 cookie name 的 value :
- maxAge 一个数字表示从 Date.now() 得到的毫秒数
- signed cookie 签名值
- expires cookie 过期的 Date
- path cookie 路径, 默认是'/'
- domain cookie 域名
- secure 安全 cookie
- httpOnly 服务器可访问 cookie, 默认是 true
- overwrite 一个布尔值,表示是否覆盖以前设置的同名的 cookie (默认是 false). 如果是 true, 在同一个请求中设置相同名称的所有 Cookie(不管路径或域)是否在设置此 Cookie 时从 Set-Cookie 标头中过滤掉。
HTTP Response 的类型
Koa 默认的返回类型是 text/plain,如果想返回其他类型的内容,可以先用 ctx.request.accepts 判断一下,客户端希望接受什么数据(根据 HTTP Request 的 Accept 字段),然后使用 ctx.response.type 指定返回类型。请看下面的例子(完整代码看这里)
const main = ctx => {
if (ctx.request.accepts('xml')) {
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);
其实可以直接通过 ctx 直接访问 response 的方法和属性,名字等价,例如:
ctx.body
ctx.body=
ctx.status
ctx.status=
ctx.message
ctx.message=
ctx.length=
ctx.length
ctx.type=
ctx.type
ctx.headerSent
ctx.redirect()
ctx.attachment()
ctx.set()
ctx.append()
ctx.remove()
ctx.lastModified=
ctx.etag=
其他参考请参与文档: https://koa.bootcss.com/
HTTP Request 的类型
Koa Request 对象是在 node 的 vanilla 请求对象之上的抽象,提供了诸多对 HTTP 服务器开发有用的功能。
request.header
获取,请求标头对象。
request.method
获取请求的方法。
request.querystring
获取请求的查询字符串
....
其他参考文档: https://koa.bootcss.com/
koa 中间件机制
koa 通过 app.use 方法注册一个中间件,中间件是一个函数。函数可以是 async 也可以是普通函数。可以接受两个参数 ctx
和 next
. ctx 是上下文, next 是执行下一个中间件。
中间件可以注册多个,每个中间件都会放到一个中间件数组中。
中间件的执行过程是: 中间件按照注册顺序依次执行,当一个中间件调用 next() 则该函数暂停并将控制传递给定义的下一个中间件。当在下游没有更多的中间件执行后,堆栈将展开并且每个中间件恢复执行其上游行为。
例如代码:
const Koa = require('koa');
const app = new Koa();
app.use( async (ctx, next) => {
console.log(1)
await next();
console.log(2)
ctx.body = 'hi, aicoder.com';
});
app.use( async (ctx, next) => {
console.log(3)
await next();
console.log(4)
});
app.use( async (ctx, next) => {
console.log(5)
await next();
console.log(6)
});
app.listen(3006);
// 请求执行打印的顺序
1
3
5
6
4
2
原理剖析:
参考文章: 深入理解 Koa2 中间件机制
静态文件中间件
静态目录的中间件, koa-static
安装
npm i -P koa-static
const Koa = require('koa')
const path = require('path')
const static = require('koa-static')
const app = new Koa()
// 静态资源目录对于相对入口文件 index.js 的路径
const staticPath = './static'
app.use(static(
path.join( __dirname, staticPath)
))
app.use( async ( ctx ) => {
ctx.body = 'hello world'
})
app.listen(3000, () => {
console.log('[demo] static-use-middleware is starting at port 3000')
})
路由中间件
路由中间件 koa-router
.
安装:
$ npm i -P koa-router
后端:
const Koa = require('koa')
const app = new Koa()
const Router = require('koa-router')
const router = new Router();
router.get('/userid/:id', async (ctx, next) => {
ctx.body = ctx.params.id; // 获取路由的参数
await next();
console.log('over')
})
// 加载路由中间件
app.use(router.routes()).use(router.allowedMethods())
app.listen(3000, () => {
console.log('[demo] route-use-middleware is starting at port 3000')
})
请求数据获取和处理
GET 请求数据获取
在 koa 中,获取 GET 请求数据源头是 koa 中 request 对象中的 query 方法或 querystring 方法,query 返回是格式化好的参数对象,querystring 返回的是请求字符串。
// server.js
const Koa = require('koa');
const Router = require('koa-router');
const app = new Koa();
const router = new Router();
router.prefix('/api');
router.get('/data', async (ctx) => {
ctx.body = ctx.query;
})
app.use(router.routes());
app.use(router.allowedMethods());
app.listen(3006);
启动
node server.js
发送请求: http://localhost:3006/api/data?id=9&name=990
返回内容:
{
id: "9",
name: "990"
}
post 请求数据处理
对于 POST
请求的处理, koa-bodyparser
中间件可以把 koa2
上下文的 formData
数据解析到 ctx.request.body
中
安装 koa2
版本的 koa-bodyparser@3
中间件
npm install --save koa-bodyparser@3
const Koa = require('koa')
const app = new Koa()
const bodyParser = require('koa-bodyparser')
// 使用 ctx.body 解析中间件
app.use(bodyParser())
app.use(async ctx => {
// the parsed body will store in ctx.request.body
// if nothing was parsed, body will be an empty object {}
ctx.body = ctx.request.body;
});
app.listen(3000, () => {
console.log('[demo] request post is starting at port 3000')
})
加载静态资源
koa-static
中间件使用.
安装:
npm i -P koa-static
const Koa = require('koa')
const path = require('path')
const static = require('koa-static')
const app = new Koa()
// 静态资源目录对于相对入口文件 index.js 的路径
const staticPath = './static'
app.use(static(
path.join( __dirname, staticPath)
))
app.use( async ( ctx ) => {
ctx.body = 'hello world'
})
app.listen(3000, () => {
console.log('[demo] static-use-middleware is starting at port 3000')
})
使用模板引擎
适用于 koa2
的模板引擎选择非常多,比如 jade
、 ejs
、 nunjucks
、 art-template
等。
art-template
是一个简约、超快的模板引擎。
art-template
支持 ejs
的语法,也可以用自己的类似 angular
数据绑定的语法
官网: http://aui.github.io/art-template/ 中文文档: http://aui.github.io/art-template/zh-cn/docs/
安装 art-template
和 koa-art-template
npm install --save art-template
npm install --save koa-art-template
配置 Koa2 中间件:
const Koa = require('koa');
const Router = require('koa-router');
const render = require('koa-art-template');
const static = require('koa-static');
const path = require('path');
const app = new Koa();
const router = new Router();
// 静态资源目录对于相对入口文件 index.js 的路径
const staticPath = './public'
app.use(static(
path.join( __dirname, staticPath)
))
render(app, {
root: path.join(__dirname, 'view'),
extname: '.art',
debug: process.env.NODE_ENV !== 'production'
})
router.prefix('/api');
router.get('/userid/:id', async (ctx, next) => {
ctx.body = ctx.params.id;
await next();
console.log('over')
})
router.get('/data', async (ctx) => {
ctx.body = ctx.query;
})
router.get('/templ', async (ctx) => {
await ctx.render('user', { name: 'aicoder.com', age: 18}); // 渲染页面
})
app.use(router.routes());
app.use(router.allowedMethods());
app.listen(3006);
目录结构:
├── app.js
├── node_modules
│──────....
├── package-lock.json
├── package.json
├── public
└── view
└── user.art
user.art
文件的内容为:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>aicoder.com</title>
</head>
<body>
<h3>name: { { name }}</h3>
<h3>age: { { age }}</h3>
</body>
</html>
请求地址: http://localhost:3006/api/templ
结果:
name: aicoder.com
age: 18
koa 中使用 cookie 和 session
koa2 使用 cookie
使用方法 koa 提供了从上下文直接读取、写入 cookie 的方法
ctx.cookies.get(name, [options]) 读取上下文请求中的 cookie
ctx.cookies.set(name, value, [options]) 在上下文中写入 cookie
koa2 中操作的 cookies 是使用了 npm 的 cookies 模块,源码在 https://github.com/pillarjs/cookies,所以在读写 cookie 的使用参数与该模块的使用一致。
例子代码
const Koa = require('koa')
const app = new Koa()
app.use( async ( ctx ) => {
if ( ctx.url === '/index' ) {
ctx.cookies.set(
'cid',
'hello world',
{
domain: 'localhost', // 写 cookie 所在的域名
path: '/index', // 写 cookie 所在的路径
maxAge: 10 * 60 * 1000, // cookie 有效时长
expires: new Date('2017-02-15'), // cookie 失效时间
httpOnly: false, // 是否只用于 http 请求中获取
overwrite: false // 是否允许重写
}
)
ctx.body = 'cookie is ok'
} else {
ctx.body = 'hello world'
}
})
app.listen(3000, () => {
console.log('[demo] cookie is starting at port 3000')
})
koa 中使用 session
koa2 原生功能只提供了 cookie 的操作,但是没有提供 session 操作。session 就只用自己实现或者通过第三方中间件实现。在 koa2 中实现 session 的方案有一下几种
- 如果 session 数据量很小,可以直接存在内存中
- 如果 session 数据量很大,则需要存储介质存放 session 数据
以下是简单 koa-session 的使用介绍
$ npm install koa-session
const session = require('koa-session');
const Koa = require('koa');
const app = new Koa();
app.keys = ['some secret hurr']; // 设置加密的字符串
const CONFIG = {
key: 'koa:sess', /** (string) cookie key (default is koa:sess) */
/** (number || 'session') maxAge in ms (default is 1 days) */
/** 'session' will result in a cookie that expires when session/browser is closed */
/** Warning: If a session cookie is stolen, this cookie will never expire */
maxAge: 86400000,
autoCommit: true, /** (boolean) automatically commit headers (default true) */
overwrite: true, /** (boolean) can overwrite or not (default true) */
httpOnly: true, /** (boolean) httpOnly or not (default true) */
signed: true, /** (boolean) signed or not (default true) */
rolling: false, /** (boolean) Force a session identifier cookie to be set on every response. The expiration is reset to the original maxAge, resetting the expiration countdown. (default is false) */
renew: false, /** (boolean) renew session when session is nearly expired, so we can always keep user logged in. (default is false)*/
};
app.use(session(CONFIG, app));
// or if you prefer all default config, just use => app.use(session(app));
app.use(ctx => {
// ignore favicon
if (ctx.path === '/favicon.ico') return;
let n = ctx.session.views || 0;
ctx.session.views = ++n;
ctx.body = n + ' views';
});
app.listen(3000);
console.log('listening on port 3000');
上传文件 multer 模块使用
类似 express 的 multer 模块, koa 下的 koa-multer 也是用于上传文件,用法跟 express 一致。
安装
$ npm install --save koa-multer
const Koa = require('koa');
const route = require('koa-route');
const multer = require('koa-multer');
const app = new Koa();
//配置
var storage = multer.diskStorage({
//文件保存路径
destination: function (req, file, cb) {
cb(null, 'public/uploads/') //注意路径必须存在
},
//修改文件名称
filename: function (req, file, cb) {
var fileFormat = (file.originalname).split(".");
cb(null,Date.now() + "_" + file.originalname);
}
})
//加载配置
var upload = multer({ storage: storage })
router.post('/doAdd', upload.single('face'), async (ctx, next) => { // html 表单上传的 input:file 标签的 name 必须是 face
ctx.body = {
filename: ctx.req.file.filename,//返回文件名
body:ctx.req.body
}
});
app.listen(3000);
允许后台跨域
Koa2 中后台允许跨域我们通过 koa2-cors 实现。
var koa = require('koa');
var cors = require('koa2-cors');
var app = koa();
app.use(cors());
分页实现
分页核心是拼接 分页组件的 a 标签。以下是 https://www.aicoder.com 内部分页组件的案例:
exports.pagination = (total, pageSize, cur, url) => {
total = total || 0;
// 一页默认多少条
pageSize = pageSize || 10
let htmlPaginition:String = '';
// 当前页的左侧显示的条数(同右侧显示的连接数)
var showLinks = 5;
if(total <= 0 ) {
return "";
}
let maxPage = Math.ceil( total / pageSize );
htmlPaginition += `<div class="pagination is-rounded is-centered" role="navigation" aria-label="pagination">`;
htmlPaginition += `<a class="pagination-previous" href="${url +'?page=1'}">首页</a>`;
htmlPaginition += `<a ${cur > 1 ? '' : 'disabled'} class="pagination-previous" href="${cur > 1 ? url +'?page=' + (cur - 1) : 'javascript:0'}">上一页</a>`;
htmlPaginition += `<a ${cur < maxPage ? '' : 'disabled'} class="pagination-next" href="${cur < maxPage ? url +'?page=' + (cur + 1) : 'javascript:0'}">下一页</a>`;
htmlPaginition += `<a class="pagination-next" href="${ url +'?page=' + maxPage }">末页</a>`;
htmlPaginition += `<ul class="pagination-list">`;
if(cur > (showLinks + 1)) {
htmlPaginition +='<li><span class="pagination-ellipsis">…</span></li>';
}
for(let i = 0; i <= ( 2 * showLinks); i++) {
if( i === showLinks ) {
htmlPaginition += ` <li><a class="pagination-link is-current">${cur}</a></li>`
continue;
}
let tempPageNum = cur - showLinks + i;
if(tempPageNum > 0 && tempPageNum <= maxPage) {
htmlPaginition += `<li><a class="pagination-link" href="${url}?page=${tempPageNum}">${tempPageNum}</a></li>`;
}
}
if( maxPage - cur > (showLinks + 1)) {
htmlPaginition +='<li><span class="pagination-ellipsis">…</span></li>';
}
htmlPaginition += `</ul>`;
htmlPaginition += `</div>`;
return htmlPaginition;
}
mongoose 的使用
Mongoose ) 是在 node.js 异步环境下对 mongodb 进行便捷操作的对象模型工具(orm); 官网: https://mongoosejs.com/
Mongoose 可以单独使用,配合 koa2 的话可以使用 koa-mongoose
中间件更加方便。
安装
npm install koa-mongoose
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论