Koa 框架教程
Koa 框架教程
作者: 阮一峰
日期: 2017年8月 9日
Node 主要用在开发 Web 应用。这决定了使用 Node,往往离不开 Web 应用框架。
Koa 就是一种简单好用的 Web 框架。它的特点是优雅、简洁、表达力强、自由度高。本身代码只有1000多行,所有功能都通过插件实现,很符合 Unix 哲学。
本文从零开始,循序渐进,教会你如何使用 Koa 写出自己的 Web 应用。每一步都有简洁易懂的示例,希望让大家一看就懂。
零、准备
首先,检查 Node 版本。
$ node -v
v8.0.0
Koa 必须使用 7.6 以上的版本。如果你的版本低于这个要求,就要先升级 Node。
然后,克隆本文的配套示例库。(如果不方便使用 Git,也可以下载 zip 文件解压。)
$ git clone https://github.com/ruanyf/koa-demos.git
接着,进入示例库,安装依赖。
$ cd koa-demos
$ npm install
所有示例源码,都在 demos 目录下面。
一、基本用法
1.1 架设 HTTP 服务
只要三行代码,就可以用 Koa 架设一个 HTTP 服务。
// demos/01.js
const Koa = require('koa');
const app = new Koa(); app.listen(3000);
运行这个脚本。
$ node demos/01.js
打开浏览器,访问 http://127.0.0.1:3000 。你会看到页面显示"Not Found",表示没有发现任何内容。这是因为我们并没有告诉 Koa 应该显示什么内容。
1.2 Context 对象
Koa 提供一个 Context 对象,表示一次对话的上下文(包括 HTTP 请求和 HTTP 回复)。通过加工这个对象,就可以控制返回给用户的内容。
Context.response.body
属性就是发送给用户的内容。请看下面的例子(完整的代码看这里)。
// demos/02.js
const Koa = require('koa');
const app = new Koa(); const main = ctx => {
ctx.response.body = 'Hello World';
}; app.use(main);
app.listen(3000);
上面代码中,main
函数用来设置ctx.response.body
。然后,使用app.use
方法加载main
函数。
你可能已经猜到了,ctx.response
代表 HTTP Response。同样地,ctx.request
代表 HTTP Request。
运行这个 demo。
$ node demos/02.js
访问 http://127.0.0.1:3000 ,现在就可以看到"Hello World"了。
1.3 HTTP Response 的类型
Koa 默认的返回类型是text/plain
,如果想返回其他类型的内容,可以先用ctx.request.accepts
判断一下,客户端希望接受什么数据(根据 HTTP Request 的Accept
字段),然后使用ctx.response.type
指定返回类型。请看下面的例子(完整代码看这里)。
// demos/03.js
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';
}
};
运行这个 demo。
$ node demos/03.js
访问 http://127.0.0.1:3000 ,现在看到的就是一个 XML 文档了。
1.4 网页模板
实际开发中,返回给用户的网页往往都写成模板文件。我们可以让 Koa 先读取模板文件,然后将这个模板返回给用户。请看下面的例子(完整代码看这里)。
// demos/04.js
const fs = require('fs'); const main = ctx => {
ctx.response.type = 'html';
ctx.response.body = fs.createReadStream('./demos/template.html');
};
运行这个 Demo。
$ node demos/04.js
访问 http://127.0.0.1:3000 ,看到的就是模板文件的内容了。
二、路由
2.1 原生路由
网站一般都有多个页面。通过ctx.request.path
可以获取用户请求的路径,由此实现简单的路由。请看下面的例子(完整代码看这里)。
// demos/05.js
const main = ctx => {
if (ctx.request.path !== '/') {
ctx.response.type = 'html';
ctx.response.body = '<a href="/">Index Page</a>';
} else {
ctx.response.body = 'Hello World';
}
};
运行这个 demo。
$ node demos/05.js
访问 http://127.0.0.1:3000/about ,可以看到一个链接,点击后就跳到首页。
2.2 koa-route 模块
原生路由用起来不太方便,我们可以使用封装好的koa-route
模块。请看下面的例子(完整代码看这里)。
// demos/06.js
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));
app.use(route.get('/about', about));
上面代码中,根路径/
的处理函数是main
,/about
路径的处理函数是about
。
运行这个 demo。
$ node demos/06.js
访问 http://127.0.0.1:3000/about ,效果与上一个例子完全相同。
2.3 静态资源
如果网站提供静态资源(图片、字体、样式表、脚本......),为它们一个个写路由就很麻烦,也没必要。koa-static
模块封装了这部分的请求。请看下面的例子(完整代码看这里)。
// demos/12.js
const path = require('path');
const serve = require('koa-static'); const main = serve(path.join(__dirname));
app.use(main);
运行这个 Demo。
$ node demos/12.js
访问 http://127.0.0.1:3000/12.js,在浏览器里就可以看到这个脚本的内容。
2.4 重定向
有些场合,服务器需要重定向(redirect)访问请求。比如,用户登陆以后,将他重定向到登陆前的页面。ctx.response.redirect()
方法可以发出一个302跳转,将用户导向另一个路由。请看下面的例子(完整代码看这里)。
// demos/13.js
const redirect = ctx => {
ctx.response.redirect('/');
ctx.response.body = '<a href="/">Index Page</a>';
}; app.use(route.get('/redirect', redirect));
运行这个 demo。
$ node demos/13.js
访问 http://127.0.0.1:3000/redirect ,浏览器会将用户导向根路由。
三、中间件
3.1 Logger 功能
Koa 的最大特色,也是最重要的一个设计,就是中间件(middleware)。为了理解中间件,我们先看一下 Logger (打印日志)功能的实现。
最简单的写法就是在main
函数里面增加一行(完整代码看这里)。
// demos/07.js
const main = ctx => {
console.log(`${Date.now()} ${ctx.request.method} ${ctx.request.url}`);
ctx.response.body = 'Hello World';
};
运行这个 Demo。
$ node demos/07.js
访问 http://127.0.0.1:3000 ,命令行就会输出日志。
1502144902843 GET /
3.2 中间件的概念
上一个例子里面的 Logger 功能,可以拆分成一个独立函数(完整代码看这里)。
// demos/08.js
const logger = (ctx, next) => {
console.log(`${Date.now()} ${ctx.request.method} ${ctx.request.url}`);
next();
}
app.use(logger);
像上面代码中的logger
函数就叫做"中间件"(middleware),因为它处在 HTTP Request 和 HTTP Response 中间,用来实现某种中间功能。app.use()
用来加载中间件。
基本上,Koa 所有的功能都是通过中间件实现的,前面例子里面的main
也是中间件。每个中间件默认接受两个参数,第一个参数是 Context 对象,第二个参数是next
函数。只要调用next
函数,就可以把执行权转交给下一个中间件。
运行这个 demo。
$ node demos/08.js
访问 http://127.0.0.1:3000 ,命令行窗口会显示与上一个例子相同的日志输出。
3.3 中间件栈
多个中间件会形成一个栈结构(middle stack),以"先进后出"(first-in-last-out)的顺序执行。
- 最外层的中间件首先执行。
- 调用
next
函数,把执行权交给下一个中间件。- ...
- 最内层的中间件最后执行。
- 执行结束后,把执行权交回上一层的中间件。
- ...
- 最外层的中间件收回执行权之后,执行
next
函数后面的代码。
请看下面的例子(完整代码看这里)。
// demos/09.js
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(one);
app.use(two);
app.use(three);
运行这个 demo。
$ node demos/09.js
访问 http://127.0.0.1:3000 ,命令行窗口会有如下输出。
>> one
>> two
>> three
<< three
<< two
<< one
如果中间件内部没有调用next
函数,那么执行权就不会传递下去。作为练习,你可以将two
函数里面next()
这一行注释掉再执行,看看会有什么结果。
3.4 异步中间件
迄今为止,所有例子的中间件都是同步的,不包含异步操作。如果有异步操作(比如读取数据库),中间件就必须写成 async 函数。请看下面的例子(完整代码看这里)。
// demos/10.js
const fs = require('fs.promised');
const Koa = require('koa');
const app = new Koa(); const main = async function (ctx, next) {
ctx.response.type = 'html';
ctx.response.body = await fs.readFile('./demos/template.html', 'utf8');
}; app.use(main);
app.listen(3000);
上面代码中,fs.readFile
是一个异步操作,必须写成await fs.readFile()
,然后中间件必须写成 async 函数。
运行这个 demo。
$ node demos/10.js
访问 http://127.0.0.1:3000 ,就可以看到模板文件的内容。
3.5 中间件的合成
koa-compose
模块可以将多个中间件合成为一个。请看下面的例子(完整代码看这里)。
// demos/11.js
const compose = require('koa-compose'); const logger = (ctx, next) => {
console.log(`${Date.now()} ${ctx.request.method} ${ctx.request.url}`);
next();
} const main = ctx => {
ctx.response.body = 'Hello World';
}; const middlewares = compose([logger, main]);
app.use(middlewares);
运行这个 demo。
$ node demos/11.js
访问 http://127.0.0.1:3000 ,就可以在命令行窗口看到日志信息。
四、错误处理
4.1 500 错误
如果代码运行过程中发生错误,我们需要把错误信息返回给用户。HTTP 协定约定这时要返回500状态码。Koa 提供了ctx.throw()
方法,用来抛出错误,ctx.throw(500)
就是抛出500错误。请看下面的例子(完整代码看这里)。
// demos/14.js
const main = ctx => {
ctx.throw(500);
};
运行这个 demo。
$ node demos/14.js
访问 http://127.0.0.1:3000,你会看到一个500错误页"Internal Server Error"。
4.2 404错误
如果将ctx.response.status
设置成404,就相当于ctx.throw(404)
,返回404错误。请看下面的例子(完整代码看这里)。
// demos/15.js
const main = ctx => {
ctx.response.status = 404;
ctx.response.body = 'Page Not Found';
};
运行这个 demo。
$ node demos/15.js
访问 http://127.0.0.1:3000 ,你就看到一个404页面"Page Not Found"。
4.3 处理错误的中间件
为了方便处理错误,最好使用try...catch
将其捕获。但是,为每个中间件都写try...catch
太麻烦,我们可以让最外层的中间件,负责所有中间件的错误处理。请看下面的例子(完整代码看这里)。
// demos/16.js
const handler = async (ctx, next) => {
try {
await next();
} catch (err) {
ctx.response.status = err.statusCode || err.status || 500;
ctx.response.body = {
message: err.message
};
}
}; const main = ctx => {
ctx.throw(500);
}; app.use(handler);
app.use(main);
运行这个 demo。
$ node demos/16.js
访问 http://127.0.0.1:3000 ,你会看到一个500页,里面有报错提示 {"message":"Internal Server Error"}
。
4.4 error 事件的监听
运行过程中一旦出错,Koa 会触发一个error
事件。监听这个事件,也可以处理错误。请看下面的例子(完整代码看这里)。
// demos/17.js
const main = ctx => {
ctx.throw(500);
}; app.on('error', (err, ctx) =>
console.error('server error', err);
);
运行这个 demo。
$ node demos/17.js
访问 http://127.0.0.1:3000 ,你会在命令行窗口看到"server error xxx"。
4.5 释放 error 事件
需要注意的是,如果错误被try...catch
捕获,就不会触发error
事件。这时,必须调用ctx.app.emit()
,手动释放error
事件,才能让监听函数生效。请看下面的例子(完整代码看这里)。
// demos/18.js`
const handler = async (ctx, next) => {
try {
await next();
} catch (err) {
ctx.response.status = err.statusCode || err.status || 500;
ctx.response.type = 'html';
ctx.response.body = '<p>Something wrong, please contact administrator.</p>';
ctx.app.emit('error', err, ctx);
}
}; const main = ctx => {
ctx.throw(500);
}; app.on('error', function(err) {
console.log('logging error ', err.message);
console.log(err);
});
上面代码中,main
函数抛出错误,被handler
函数捕获。catch
代码块里面使用ctx.app.emit()
手动释放error
事件,才能让监听函数监听到。
运行这个 demo。
$ node demos/18.js
访问 http://127.0.0.1:3000 ,你会在命令行窗口看到logging error
。
五、Web App 的功能
5.1 Cookies
ctx.cookies
用来读写 Cookie。请看下面的例子(完整代码看这里)。
// demos/19.js
const main = function(ctx) {
const n = Number(ctx.cookies.get('view') || 0) + 1;
ctx.cookies.set('view', n);
ctx.response.body = n + ' views';
}
运行这个 demo。
$ node demos/19.js
访问 http://127.0.0.1:3000 ,你会看到1 views
。刷新一次页面,就变成了2 views
。再刷新,每次都会计数增加1。
5.2 表单
Web 应用离不开处理表单。本质上,表单就是 POST 方法发送到服务器的键值对。koa-body
模块可以用来从 POST 请求的数据体里面提取键值对。请看下面的例子(完整代码看这里)。
// demos/20.js
const koaBody = require('koa-body'); 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());
运行这个 demo。
$ node demos/20.js
打开另一个命令行窗口,运行下面的命令。
$ curl -X POST --data "name=Jack" 127.0.0.1:3000
{"name":"Jack"} $ curl -X POST --data "name" 127.0.0.1:3000
name required
上面代码使用 POST 方法向服务器发送一个键值对,会被正确解析。如果发送的数据不正确,就会收到错误提示。
2.3 文件上传
koa-body
模块还可以用来处理文件上传。请看下面的例子(完整代码看这里)。
// demos/21.js
const os = require('os');
const path = require('path');
const koaBody = require('koa-body'); const main = async function(ctx) {
const tmpdir = os.tmpdir();
const filePaths = [];
const files = ctx.request.body.files || {}; for (let key in files) {
const file = files[key];
const filePath = path.join(tmpdir, file.name);
const reader = fs.createReadStream(file.path);
const writer = fs.createWriteStream(filePath);
reader.pipe(writer);
filePaths.push(filePath);
} ctx.body = filePaths;
}; app.use(koaBody({ multipart: true }));
运行这个 demo。
$ node demos/21.js
打开另一个命令行窗口,运行下面的命令,上传一个文件。注意,/path/to/file
要更换为真实的文件路径。
$ curl --form upload=@/path/to/file http://127.0.0.1:3000
["/tmp/file"]
六、参考链接
(完)
文档信息
- 版权声明:自由转载-非商用-非衍生-保持署名(创意共享3.0许可证)
- 发表日期: 2017年8月 9日
- 更多内容: 档案 » JavaScript
- 文集:《前方的路》,《未来世界的幸存者》
- 社交媒体:aaarticlea/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAk1JREFUeNqsVc9rE0EU/mZ3dhPNJl2bVIk/Km09aFBE8GLvnrwJXvwLxIsgCP4Fnit6E8Gb0IMHwUsL9SSNgiAoooJIpE1Joo1h82uT7IxvxmRN3Q32kAdvdrI773vfe+/bDTu9WioDcDAda3Ja8piepQ1avCkCeny0Y+R9KenKwOmHHLvPaKn7AtVOoO8dOWDCMpg+41gMQv5FNEJACgoE8MsPNLDBhgfoutMOsJDmWLmU037C4TApciljwQ/kHoohQxV06+wMXNvE7WINuaSpvdYVKLgWnl3O4zAxU3Z1IYVGT+B5qYWV9z2kbQaT/cOwTsxOUUYF+mB5Dkmi8HG3h2q9hxsFNwRTdvQgxxnXRrMvIdkEhllis/qtiWuLDm4WZnBlPoWnXz28LHdw0uGR7m9Wu3j0qYGEubfn4UlBfXO4gTZltRNMg9w9f0i7kNFxllsBlS0x75gYfxyWTBUjT2W5CSMSPBrQuG21+hiMDS8CeCzF8eSLp13uQ3Ab1IqMFZN8tFHPmgOBF99bYP8Be1Xp6t7OJszJgKpPSiZvaj7uf2hguz2IBQuozDvFH6RDBjuK9wdQDvt0nMpW+8efGyh5UcAeZb2+UcHbnz5Jx4wdlp4yJYU3kLiQtfBwOYeLc8nIwfXtNu69q2Oz4mMxbemYOOOjKarhvq51KUjg3GwiFPIuvcNF6pnSnWK0lOEabNLgQh1aJFAhlGB9rG110B2+oyRNPc0sZbRNFltmLKDKqj4Qrm1ojzOxDz2pyPQ0P7CK4c40/wJ+CzAAGYrXsvfFXR4AAAAASUVORK5CYII=" alt="" /> twitter,aaarticlea/png;base64,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" alt="" /> weibo
相关文章
- 2018.07.08: Web Worker 使用教程
一、概述
- 2018.07.04: 浏览器数据库 IndexedDB 入门教程
一、概述
- 2018.06.18: JavaScript 的 this 原理
一、问题的由来 学懂 JavaScript 语言,一个标志就是理解下面两种写法,可能有不一样的结果。
- 2018.03.20: Node 调试工具入门教程
JavaScript 程序越来越复杂,调试工具的重要性日益凸显。客户端脚本有浏览器,Node 脚本怎么调试呢?
广告(购买广告位)
留言(92条)
阮荣军 说:
passport 官网 http://www.passportjs.org/
alisx 说:
在5.2的代码示例中第4行
------------------
1 // demos/20.js
2 const koaBody = require('koa-body');
3
4 const main = async function(ctx) {
5 const body = ctx.request.body;
6 if (!body.name) ctx.throw(400, '.name required');
7 ctx.body = { name: body.name };
8 };
-------------------
main 是个异步方法,但从代码来看,没有异步的过程,请问
1 是否没有必要声明 async
2 是否出于未来代码变更考虑,将所有的方法都声明为 async,这样是好还是不好?会有性能或其他问题吗?
谢谢!
IndraNyo 说:
重定向这里
ctx.response.redirect('/');
ctx.response.body = 'Index Page';
第二行好像不执行 已经重定向走了 应该在 route.get('/', XXX)的route里写 才会输出吧
还是我操作不对?请老师指教
hooper 说:
引用disguiser的发言:不是很理解为什么要把多个中间件合成为一个
多此一举
Koa 框架教程的更多相关文章
- Koa框架教程,Koa框架开发指南,Koa框架中文使用手册,Koa框架中文文档
我的博客:CODE大全:www.codedq.net:业余草:www.xttblog.com:爱分享:www.ndislwf.com或ifxvn.com. Koa -- 基于 Node.js 平台的下 ...
- Koa框架教程
Node主要用在开发 Web 应用.这决定了使用 Node,往往离不开 Web 应用框架. Koa 就是一种简单好用的 Web 框架.它的特点是优雅.简洁.表达力强.自由度高.本身代码只有1000多行 ...
- tinyshop框架教程已在腾讯课堂开课
php就业网简介:www.php91.net,专注于Thinkphp框架教程的php框架学习中心.同时也有小崔老师自学php的教程,与你一起成长哦 同时,php就业网教程部分:http://www.p ...
- koa框架异步返回值的操作(co,koa-compose)
最近在做demo的时候使用了koa框架,自己做了一个静态服务器,首先判断访问文件是否存在,在回调函数中设置了this.body,run之后,各种404,花了N长的时间把koa-compose和co模块 ...
- nodejs 中koa框架下的微信公众号开发初始篇
最近在搞微信公众号开发,后端采用的是nodejs下的koa框架,初识后端的菜鸟,自己搞难度太大了,网上找了很多文章,采用的中间件大都是express框架下的,不过好在爬了许多坑之后总算看见点曙光了,遂 ...
- jfinal框架教程-学习笔记
jfinal框架教程-学习笔记 JFinal 是基于 Java 语言的极速 WEB + ORM 开发框架,其核心设计目标是开发迅速.代码量少.学习简单.功能强大.轻量级.易扩展.Restfu ...
- revel框架教程之缓存和Job
Go语言实战 - revel框架教程之缓存和Job 所有的网站应该都会有一个非常简单的需求,首页一秒之内打开. 满足的方式主要有两种: 页面静态化,效果最好,对服务器基本没负担,只要带宽足够就好了 ...
- revel框架教程之权限控制
Go语言实战 - revel框架教程之权限控制 一个站点上面最基本都会有三种用户角色,未登录用户.已登录用户和管理员.这一次我们就来看看在revel框架下如何进行权限控制. 因为revel是MVC结构 ...
- scrapy爬虫框架教程(二)-- 爬取豆瓣电影TOP250
scrapy爬虫框架教程(二)-- 爬取豆瓣电影TOP250 前言 经过上一篇教程我们已经大致了解了Scrapy的基本情况,并写了一个简单的小demo.这次我会以爬取豆瓣电影TOP250为例进一步为大 ...
随机推荐
- Linux firewalld使用教程+rhce课程实验
--timeout= 设置规则生效300秒 调试阶段使用,防止规则设置错误导致无法远程连接 实验:在server0机器上部署httpd服务,通过添加富规则,只允许172.25.0.10/32访问,并且 ...
- D. Kilani and the Game(多源BFS)
题目来源:http://codeforces.com/contest/1105/problem/D 题意:编号为1-k的点在一张n*m的表格中依次扩散,每个结点有各自的扩散速度且只可以往上下左右四个方 ...
- 网络基础-- 之 子网划分 and 一些基础解释
子网划分的核心思想就是------ 借主机为为网络位 最近几天--看了一下今天就分享一波. 首先我们先来理解一下 ----- one. 进制的转换 ----- two. IP地址 -- ...
- 396. Rotate Function 移动加权求和,取最大值
[抄题]: Given an array of integers A and let n to be its length. Assume Bk to be an array obtained by ...
- [leetcode]13. Roman to Integer罗马数字转整数
Roman numerals are represented by seven different symbols: I, V, X, L, C, D and M. Symbol Value I 1 ...
- php curl请求回来的中文为乱码
在浏览器访问回来的编码格式是正常的,但是从php curl 请求过来的确实乱码,之前也试过这个函数好像不行,今天吧最后一个参数换了,简单粗暴,可以了mb_convert_encoding($res, ...
- Mybatis配置问题解决Invalid bound statement (not found)
首先这个异常的原因是系统根据Mapper类的方法名找不到对应的映射文件. 网上也搜索了到了类似的文章,一般可以从以下几个点排查: mapper.xml的namespace要写所映射接口的全称类名,而且 ...
- jquery.validate.js的简单示例
一.官方资料 官网 github 二.html <!DOCTYPE html> <html> <head> <meta charset="utf-8 ...
- 2018-10-19,下午4点拿到京东offer
今天12点没有问题就签.岗位,地点都很满足.京东居然对我这么好,保证以后不做损坏京东利益的事写给以后的自己.
- Sublime Text快捷键与插件介绍
Sublime Text快捷键: Ctrl+Shift+P:打开命令面板Ctrl+P:搜索项目中的文件Ctrl+G:跳转到第几行Ctrl+W:关闭当前打开文件Ctrl+Shift+W:关闭所有打开文件 ...