Koa 框架教程

扫码查看

Koa 框架教程

 

作者: 阮一峰

日期: 2017年8月 9日

Koa 框架教程-LMLPHP

Node 主要用在开发 Web 应用。这决定了使用 Node,往往离不开 Web 应用框架。

Koa 框架教程-LMLPHP

Koa 就是一种简单好用的 Web 框架。它的特点是优雅、简洁、表达力强、自由度高。本身代码只有1000多行,所有功能都通过插件实现,很符合 Unix 哲学。

本文从零开始,循序渐进,教会你如何使用 Koa 写出自己的 Web 应用。每一步都有简洁易懂的示例,希望让大家一看就懂。

零、准备

首先,检查 Node 版本。

Koa 必须使用 7.6 以上的版本。如果你的版本低于这个要求,就要先升级 Node。

然后,克隆本文的配套示例库。(如果不方便使用 Git,也可以下载 zip 文件解压。)

接着,进入示例库,安装依赖。

所有示例源码,都在 demos 目录下面。

一、基本用法

1.1 架设 HTTP 服务

只要三行代码,就可以用 Koa 架设一个 HTTP 服务。

运行这个脚本。

打开浏览器,访问 http://127.0.0.1:3000 。你会看到页面显示"Not Found",表示没有发现任何内容。这是因为我们并没有告诉 Koa 应该显示什么内容。

Koa 框架教程-LMLPHP

1.2 Context 对象

Koa 提供一个 Context 对象,表示一次对话的上下文(包括 HTTP 请求和 HTTP 回复)。通过加工这个对象,就可以控制返回给用户的内容。

Context.response.body属性就是发送给用户的内容。请看下面的例子(完整的代码看这里)。

上面代码中,main函数用来设置ctx.response.body。然后,使用app.use方法加载main函数。

你可能已经猜到了,ctx.response代表 HTTP Response。同样地,ctx.request代表 HTTP Request。

运行这个 demo。

访问 http://127.0.0.1:3000 ,现在就可以看到"Hello World"了。

Koa 框架教程-LMLPHP

1.3 HTTP Response 的类型

Koa 默认的返回类型是text/plain,如果想返回其他类型的内容,可以先用ctx.request.accepts判断一下,客户端希望接受什么数据(根据 HTTP Request 的Accept字段),然后使用ctx.response.type指定返回类型。请看下面的例子(完整代码看这里)。

运行这个 demo。

访问 http://127.0.0.1:3000 ,现在看到的就是一个 XML 文档了。

Koa 框架教程-LMLPHP

1.4 网页模板

实际开发中,返回给用户的网页往往都写成模板文件。我们可以让 Koa 先读取模板文件,然后将这个模板返回给用户。请看下面的例子(完整代码看这里)。

运行这个 Demo。

访问 http://127.0.0.1:3000 ,看到的就是模板文件的内容了。

Koa 框架教程-LMLPHP

二、路由

2.1 原生路由

网站一般都有多个页面。通过ctx.request.path可以获取用户请求的路径,由此实现简单的路由。请看下面的例子(完整代码看这里)。

运行这个 demo。

访问 http://127.0.0.1:3000/about ,可以看到一个链接,点击后就跳到首页。

Koa 框架教程-LMLPHP

2.2 koa-route 模块

原生路由用起来不太方便,我们可以使用封装好的koa-route模块。请看下面的例子(完整代码看这里)。

上面代码中,根路径/的处理函数是main/about路径的处理函数是about

运行这个 demo。

访问 http://127.0.0.1:3000/about ,效果与上一个例子完全相同。

2.3 静态资源

如果网站提供静态资源(图片、字体、样式表、脚本......),为它们一个个写路由就很麻烦,也没必要。koa-static模块封装了这部分的请求。请看下面的例子(完整代码看这里)。

运行这个 Demo。

访问 http://127.0.0.1:3000/12.js,在浏览器里就可以看到这个脚本的内容。

2.4 重定向

有些场合,服务器需要重定向(redirect)访问请求。比如,用户登陆以后,将他重定向到登陆前的页面。ctx.response.redirect()方法可以发出一个302跳转,将用户导向另一个路由。请看下面的例子(完整代码看这里)。

运行这个 demo。

访问 http://127.0.0.1:3000/redirect ,浏览器会将用户导向根路由。

三、中间件

3.1 Logger 功能

Koa 的最大特色,也是最重要的一个设计,就是中间件(middleware)。为了理解中间件,我们先看一下 Logger (打印日志)功能的实现。

最简单的写法就是在main函数里面增加一行(完整代码看这里)。

运行这个 Demo。

访问 http://127.0.0.1:3000 ,命令行就会输出日志。

3.2 中间件的概念

上一个例子里面的 Logger 功能,可以拆分成一个独立函数(完整代码看这里)。

像上面代码中的logger函数就叫做"中间件"(middleware),因为它处在 HTTP Request 和 HTTP Response 中间,用来实现某种中间功能。app.use()用来加载中间件。

基本上,Koa 所有的功能都是通过中间件实现的,前面例子里面的main也是中间件。每个中间件默认接受两个参数,第一个参数是 Context 对象,第二个参数是next函数。只要调用next函数,就可以把执行权转交给下一个中间件。

运行这个 demo。

访问 http://127.0.0.1:3000 ,命令行窗口会显示与上一个例子相同的日志输出。

3.3 中间件栈

多个中间件会形成一个栈结构(middle stack),以"先进后出"(first-in-last-out)的顺序执行。

请看下面的例子(完整代码看这里)。

运行这个 demo。

访问 http://127.0.0.1:3000 ,命令行窗口会有如下输出。

如果中间件内部没有调用next函数,那么执行权就不会传递下去。作为练习,你可以将two函数里面next()这一行注释掉再执行,看看会有什么结果。

3.4 异步中间件

迄今为止,所有例子的中间件都是同步的,不包含异步操作。如果有异步操作(比如读取数据库),中间件就必须写成 async 函数。请看下面的例子(完整代码看这里)。

上面代码中,fs.readFile是一个异步操作,必须写成await fs.readFile(),然后中间件必须写成 async 函数。

运行这个 demo。

访问 http://127.0.0.1:3000 ,就可以看到模板文件的内容。

3.5 中间件的合成

koa-compose模块可以将多个中间件合成为一个。请看下面的例子(完整代码看这里)。

运行这个 demo。

访问 http://127.0.0.1:3000 ,就可以在命令行窗口看到日志信息。

四、错误处理

4.1 500 错误

如果代码运行过程中发生错误,我们需要把错误信息返回给用户。HTTP 协定约定这时要返回500状态码。Koa 提供了ctx.throw()方法,用来抛出错误,ctx.throw(500)就是抛出500错误。请看下面的例子(完整代码看这里)。

运行这个 demo。

访问 http://127.0.0.1:3000,你会看到一个500错误页"Internal Server Error"。

Koa 框架教程-LMLPHP

4.2 404错误

如果将ctx.response.status设置成404,就相当于ctx.throw(404),返回404错误。请看下面的例子(完整代码看这里)。

运行这个 demo。

访问 http://127.0.0.1:3000 ,你就看到一个404页面"Page Not Found"。

Koa 框架教程-LMLPHP

4.3 处理错误的中间件

为了方便处理错误,最好使用try...catch将其捕获。但是,为每个中间件都写try...catch太麻烦,我们可以让最外层的中间件,负责所有中间件的错误处理。请看下面的例子(完整代码看这里)。

运行这个 demo。

访问 http://127.0.0.1:3000 ,你会看到一个500页,里面有报错提示 {"message":"Internal Server Error"}

Koa 框架教程-LMLPHP

4.4 error 事件的监听

运行过程中一旦出错,Koa 会触发一个error事件。监听这个事件,也可以处理错误。请看下面的例子(完整代码看这里)。

运行这个 demo。

访问 http://127.0.0.1:3000 ,你会在命令行窗口看到"server error xxx"。

4.5 释放 error 事件

需要注意的是,如果错误被try...catch捕获,就不会触发error事件。这时,必须调用ctx.app.emit(),手动释放error事件,才能让监听函数生效。请看下面的例子(完整代码看这里)。

上面代码中,main函数抛出错误,被handler函数捕获。catch代码块里面使用ctx.app.emit()手动释放error事件,才能让监听函数监听到。

运行这个 demo。

访问 http://127.0.0.1:3000 ,你会在命令行窗口看到logging error

五、Web App 的功能

5.1 Cookies

ctx.cookies用来读写 Cookie。请看下面的例子(完整代码看这里)。

运行这个 demo。

访问 http://127.0.0.1:3000 ,你会看到1 views。刷新一次页面,就变成了2 views。再刷新,每次都会计数增加1。

Koa 框架教程-LMLPHP

5.2 表单

Web 应用离不开处理表单。本质上,表单就是 POST 方法发送到服务器的键值对。koa-body模块可以用来从 POST 请求的数据体里面提取键值对。请看下面的例子(完整代码看这里)。

运行这个 demo。

打开另一个命令行窗口,运行下面的命令。

上面代码使用 POST 方法向服务器发送一个键值对,会被正确解析。如果发送的数据不正确,就会收到错误提示。

2.3 文件上传

koa-body模块还可以用来处理文件上传。请看下面的例子(完整代码看这里)。

运行这个 demo。

打开另一个命令行窗口,运行下面的命令,上传一个文件。注意,/path/to/file要更换为真实的文件路径。

六、参考链接

(完)

文档信息

Koa 框架教程-LMLPHP

Koa 框架教程-LMLPHP

相关文章

留言(92条)

龙卷 说:

正在用koa 教程这就来了!

 | # | 引用

韩帅 说:

简单易用

 | # | 引用

业余草 说:

讲的很详细!
Koa -- 基于 Node.js 平台的下一代 web 开发框架!

 | # | 引用

陈恺垣 说:

感谢,之前一直在用express

 | # | 引用

理斯特 说:

阮老师为啥不直接介绍 egg.js

 | # | 引用

disguiser 说:

不是很理解为什么要把多个中间件合成为一个

 | # | 引用

MarshallStan 说:

感谢阮一峰老师。

 | # | 引用

wuyang 说:

讲的很详细 赞

 | # | 引用

shadow 说:

感谢分享~前几天刚好在入门Koa2

 | # | 引用

csjiabin 说:

koa和express哪个比较好 我现在一直在用express 听说koa小 想试试

 | # | 引用

 说:

不太理解为何要做中间件的合成(koa-compose),阮一峰老师,不知这有什么应用的场景?既然都是中间件,都是按顺序执行,为何还有做合成?是方便多个中间件,有公用的部分,可多次应用在不同中间件上?

 | # | 引用

诗与远方 说:

正在学习koa2!

 | # | 引用

magicbing 说:

感觉koa和express都是简洁的小框架。
像ror或者django那种大框架好像有sails和adonis,不过感觉也不是很成熟

 | # | 引用

CODE大全 说:

阮老师的文章都是图文并茂,而且demo和源码都很详细!koa框架正在学习中!

 | # | 引用

cshenger 说:

阮老师的入门系列一向简介明了

 | # | 引用

cdd 说:

我记得koa文档中koa.request/koa.response是其自身封装的对象吧。koa.req/koa.res是node的对象。

 | # | 引用

李家梁 说:

我用koa2几个月了,
路由用的最多的是koa-router,
文件上传可以试试koa-multer

 | # | 引用

久伴我暖 说:

膜拜一下前端界的大佬。分享了最新又是值得学习的框架,学习前留个言。

 | # | 引用

MorningTZH 说:

哈哈哈 我也是,瞌睡送枕头呢。

 | # | 引用

宇宙全栈 说:

是时候从 express 换到 koa 了!

 | # | 引用

Naomi 说:

node 检查出来是6的版本,但是用brew更新的是8的版本,会有影响吗

 | # | 引用

受益匪浅

 | # | 引用

阮荣军 说:

Mr.阮,能来一发passport的教程么,结合koa 来一发koa-passport模块详细使用教程.

passport 官网 http://www.passportjs.org/

 | # | 引用

安晗 说:

非常想提升一下自身作为web前端的能力和素质,看了一些帖子,都推荐来看一下阮老师的教程,以后我会多多关注您,把您的教程都好好学习,祝阮老师身体健康,为我们写出更多的精品教程。

 | # | 引用

feilong 说:

Error: Cannot find module 'koa' = =,老师怎么回事?

 | # | 引用

桂梅桑 说:

清晰明了~

 | # | 引用

coldxu 说:

阮老师写的太全了,几乎初学所有点都涵盖到了,感谢。

 | # | 引用

Phlip 说:

阮老师,求分享算法和互联网架构的内容。

 | # | 引用

刘煜 说:

你没有安装依赖,在node demos/01.js之前,你需要命令行执行npm install

 | # | 引用

distance 说:

好小巧啊~~以前一点点地开始学node.js感觉什么都要自己考虑到,各种处理。后来看了express,使用起来方便多了,也很小巧~~现在又在这里看了koa

 | # | 引用

vista-hey 说:

阮老师 ··又是4个多月才等的您的一次更新··但是值得

 | # | 引用

wells 说:

官方文档是这样

 | # | 引用

van 说:

3.5 中件 是中间件吧,少打了一个字。

 | # | 引用

阮一峰 说:

@van:谢谢指出,已经改正了。

 | # | 引用

kindhj 说:

数据库逻辑处理呢。。?本人小白

 | # | 引用

bianhognfei 说:

阮哥你好,你都用什么工具写博客的,我现在也想做一个自己的博客,来写博客。

 | # | 引用

ovnrain 说:

用Koa自己写一个啊^_^

 | # | 引用

谢东磊 说:

const handler = async (ctx, next) => {
^
SyntaxError: Unexpected token

好几个例子报这样的错误是怎么回事

 | # | 引用

~~~ 说:

@谢东磊:

不识别箭头函数吧

 | # | 引用

mdz 说:

为什么路由介绍使用的是koa-route没有使用koa-router

 | # | 引用

十三 说:

koa2 因为需要async await 的原因需要node版本在7以上,koa1不需要

 | # | 引用

hdp 说:

感觉你node版本太低

 | # | 引用

linzx 说:

DeprecationWarning: Calling an asynchronous function wit
hout callback is deprecated.

3.4节使用async异步编程那里报了这个错,查了下,说这个API废弃了,大家是怎么解决的啊

 | # | 引用

达文西12138 说:

有类似Express那种已经搭建好一点的koa框架模板吗?一些404,路由等都配置好了的

 | # | 引用

张志成 说:

是koa-router不是koa-route,少了个r

 | # | 引用

张志成 说:

说错了,这两个插件都存在,koa-route貌似是官方的,但是koa-router的star多一点

 | # | 引用

老黄 说:

sequelize

 | # | 引用

用koa架设http服务器的,来看下,以后可能会用到

 | # | 引用

microbingbing 说:

为什么是下一代?比express有什么优势么?

 | # | 引用

小涛 说:

好像有个小的笔误,应该是koa-router, 而不是koa-route

 | # | 引用

顿顿 说:

06.js 这句是多余的?

app.use(main);

 | # | 引用

G小健 说:

比官网讲得好。每次有什么问题不懂,发现阮老师写过日志,就安心了

 | # | 引用

hackjay 说:

确实看到你的文章比较多,谢谢你的分享,会继续关注你的,希望出些实用的高级课程学学

 | # | 引用

GS 说:

koa-route 和 koa-router 两个太像了!阮一峰用的 koa-route, 廖雪峰用的 koa-router

 | # | 引用

JQ_Chan 说:

我想请问下在异步中间件的demo中main前面还可以增加其他中间件么?怎么加了好像会报错

 | # | 引用

stillyu 说:

5.2表单
ctx.body = ''
写错了

 | # | 引用

王 说:

koa比express好很多,正在学习node,发现如果用express框架编写程序是要私人的感觉,各种回掉,还是koa比较正常一点,受教了!

 | # | 引用

青伢子 说:

两个都是可以的

 | # | 引用

金牌红豆 说:

太棒了!受益匪浅!! 前端学习这个感觉像打开了新世界的大门!!感谢阮老师

 | # | 引用

姜珊 说:

Koa是默认127.0.0.1的主机名吗?在哪里可以更改

 | # | 引用

姜珊 说:

koa可以处理PUT请求吗?

 | # | 引用

杨毅 说:

最后一节的用来上传的那里有点不明白,可以展示一个跟html前台页面结合的上传代码吗

 | # | 引用

ranyingxia 说:

求出一个 koa 与 MongoDB 结合的 demo 教程

 | # | 引用

王泽 说:

老师您好,我想问下,demos/09.js的示例为什么我执行时触发了两次中间件?我看您的运行结果也只是触发一次呀

 | # | 引用

k 说:

demos/05.js 与 demos/06.js 不完全一样。http://127.0.0.1:3000/aboux 的结果就不一样。

 | # | 引用

alisx 说:

5.2 的示例中,中间件main里,并没有 await 表示,为什么main函数需要声明成 async?

 | # | 引用

alisx 说:

我去掉了main的async声明,结果同样有效

 | # | 引用

lp 说:

同意!!!!!
求解

 | # | 引用

好久不见 说:

用了好久的express框架,突然需要做一个blog,是基于koa并且用render后端渲染,确实koa是大势所趋

 | # | 引用

Sumii 说:

懂了好多,哈哈谢谢!

 | # | 引用

1Q84 说:

koa里可以用bootstrap吗?目前没找到较为详细的教程,使用中遇到很多问题

 | # | 引用

ben 说:

求解10.js运行报错是什么原因呢 Unexpected token function

 | # | 引用

杨毅 说:

使用koa-body上传文件的时候,无法获取其他表单的值,这个要怎么办?

 | # | 引用

小迈克 说:

一年前还看不懂阮一峰老师的教程,现在感觉简单易懂。

 | # | 引用

嘿嘿 说:

经常看阮一峰老师的博客,受益很多

 | # | 引用

Casey 说:

$ curl -X POST --data "name=Jack" 127.0.0.1:3000
{"name":"Jack"}\

curl不是内部或外部命令,这个怎么解决,在window7下

 | # | 引用

灵谷 说:

今天发了一上午看完,教程简洁明了!入门法宝!非常谢谢老师!在你这里学到了很多东西!学会了很多!所谓“师傅引进门,修行看个人”,在前端这个行业,并没有系统的专业教育(反正我们学校没有),全靠自学和培训~-~。继续奋斗吧!

 | # | 引用

wopelo 说:

@lp:

我感觉是因为如果有多个中间件需要使用,那么就得写多个app.use(),使用koa-compose的话直接把中间件写到参数中,最后只需要写一个app.use()即可

 | # | 引用

alisx 说:

在5.2的代码示例中第4行
------------------
1 // demos/20.js
2 const koaBody = require('koa-body');

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,这样是好还是不好?会有性能或其他问题吗?

谢谢!

 | # | 引用

天天 说:

同感!

 | # | 引用

scs 说:

看了后,有了初步的了解,比较简洁。

 | # | 引用

dearest 说:

node 必须用7.6以上的版本, 写成了 “Koa 必须使用 7.6 以上的版本”

 | # | 引用

青格勒 说:

我在项目中也开始使用koa了

 | # | 引用

yinsang 说:

5.1 Cookies里面
由于favicon.icon 的http会执行两次,所以views前的数变成1、3、5、7。
需要用route规避一下

 | # | 引用

IndraNyo 说:

重定向这里
ctx.response.redirect('/');
ctx.response.body = 'Index Page';

第二行好像不执行 已经重定向走了 应该在 route.get('/', XXX)的route里写 才会输出吧
还是我操作不对?请老师指教

 | # | 引用

jackletter 说:

“3.4 异步中间件”部分,
await fs.readFile('./demos/template.html', 'utf8')
显示报错,改成await fs.readFileSync('package.json','utf-8');就好了

 | # | 引用

Ed Me 说:

koa-router 好像更利于项目工程

 | # | 引用

Jack Chen 说:

阮老师小弟是看你博客长大的

 | # | 引用

郭小帅 说:

我测试了一下,实际上是有执行,但是执行之后被重定向的内容又覆盖掉了。

 | # | 引用

意外金喜 说:

fs.readFile是回调的方法吧,await后面需要返回 Promise ,回调会报错。

 | # | 引用

xiaohuangmao 说:

通俗易懂

 | # | 引用

hooper 说:

多此一举

04-17 04:18
查看更多