前言
学习来源:廖雪峰教程
开发工具:WebStorm
前几天看egg.js,门都入不了,(lll¬ω¬),放弃,既然egg在koa基础发展而来,那我就看koa,简单记录下练习过程,不包含具体细节。
正文
首先,我们创建一个目录hello_koa并作为工程目录。然后,我们创建app.js,输入以下代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| // 导入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...');
|
因为上一个项目用的express,koa又是基于express发展来的,所以看着这一段代码还是比较好理解的。
对于每一个http请求,koa将调用我们传入的异步函数来处理:
1 2 3 4 5 6 7
| async (ctx, next) => { await next(); // 设置response的Content-Type: ctx.response.type = 'text/html'; // 设置response的内容: ctx.response.body = '<h1>Hello, koa2!</h1>'; }
|
上面的异步函数中,我们首先用await next();
处理下一个异步函数,然后,设置response的Content-Type和内容。
安装koa
- 可以用npm命令直接安装koa。先打开命令提示符,务必把当前目录切换到hello-koa这个目录,然后执行命令。
- 在hello-koa这个目录下创建一个package.json,这个文件描述了我们的hello-koa工程会用到哪些包。完整的文件内容如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| { "name": "hello-koa", "version": "1.0.0", "description": "Hello Koa 2 example with async", "main": "app.js", "scripts": { "start": "node app.js" }, "keywords": [ "koa", "async" ], "author": "Michael Liao", "license": "Apache-2.0", "repository": { "type": "git", "url": "https://github.com/michaelliao/learn-javascript.git" }, "dependencies": { "koa": "2.0.0" } }
|
然后,我们在hello-koa目录下执行npm install就可以把所需包以及依赖包一次性全部装好。
现在,我们的工程结构如下:
1 2 3 4 5 6 7
| hello_koa/ | +- app.js <-- 使用koa的js | +- package.json <-- 项目描述文件 | +- node_modules/ <-- npm安装的所有依赖包
|
执行程序
还可以直接用命令node app.js在命令行启动程序,或者用npm start启动。npm start命令会让npm执行定义在package.json文件中的start对应命令:
1 2 3
| "scripts": { "start": "node app.js" }
|
koa middleware
每收到一个http请求,koa就会调用通过app.use()注册的async函数,并传入ctx和next参数。
我们可以对ctx操作,并设置返回内容。但是为什么要调用await next()?
原因是koa把很多async函数组成一个处理链,每个async函数都可以做一些自己的事情,然后用await next()来调用下一个async函数。我们把每个async函数称为middleware,这些middleware可以组合起来,完成很多有用的功能。
例如,可以用以下3个middleware组成处理链,依次打印日志,记录处理时间,输出HTML:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| 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>'; });
|
处理URL
此时,随便输入任何URL都会返回相同的网页,我们需要进一步处理:
为了处理URL,我们需要引入koa-router这个middleware,让它负责处理URL映射。
先在package.json中添加依赖项:"koa-router": "7.0.0"
,然后用npm install安装。
接下来,我们修改app.js,使用koa-router来处理URL:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| const Koa = require('koa'); // 注意require('koa-router')返回的是函数: const router = require('koa-router')(); const app = new Koa(); // log request URL: app.use(async (ctx, next) => { console.log(`Process ${ctx.request.method} ${ctx.request.url}...`); await next(); }); // add url-route: router.get('/hello/:name', async (ctx, next) => { var name = ctx.params.name; ctx.response.body = `<h1>Hello, ${name}!</h1>`; }); router.get('/', async (ctx, next) => { ctx.response.body = '<h1>Index</h1>'; }); // add router middleware: app.use(router.routes()); app.listen(3000); console.log('app started at port 3000...');
|
用router.get(‘/path’, async fn)处理的是get请求。如果要处理post请求,可以用router.post(‘/path’, async fn)。
用post请求处理URL时,我们会遇到一个问题:post请求通常会发送一个表单,或者JSON,它作为request的body发送,但无论是Node.js提供的原始request对象,还是koa提供的request对象,都不提供解析request的body的功能!
所以,我们又需要引入另一个middleware来解析原始request请求,然后,把解析后的参数,绑定到ctx.request.body中。
koa-bodyparser就是用来干这个活的。
我们在package.json中添加依赖项:"koa-bodyparser": "3.2.0"
,然后使用npm install安装
下面,修改app.js,引入koa-bodyparser:
1
| const bodyParser = require('koa-bodyparser');
|
由于middleware的顺序很重要,这个koa-bodyparser必须在router之前被注册到app对象上。
现在我们就可以处理post请求了。写一个简单的登录表单:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| 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) => { var name = ctx.request.body.name || '', 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>`; } });
|
此时运行代码,就可以跑起来一个简单的登陆功能,此时状态欠佳,明天再整理重构代码,主要是讲url处理这块提出来优化下。