node_06 web開發

koa是Express的下一代基于Node.js的web框架,目前有1.x和2.0两个版本。

历史

1. Express

Express是第一代最流行的web框架,它对Node.js的http进行了封装,用起来如下:
\color{red}{腳本}(index.js)

    var express = require('express');
    var app = express();

    app.get('/', function (req, res) {
        res.send('Hello World!');
    });

    app.listen(3000, function () {
        console.log('Example app listening on http://127.0.0.1:3000!');
    });

命令行結果:

Example app listening on http://127.0.0.1:3000!

web結果:

Hello World!

虽然Express的API很简单,但是它是基于ES5的语法,要实现异步代码,只有一个方法:回调。如果异步嵌套层次过多,代码写起来就非常难看:
\color{red}{腳本}(index1.js)

    var express = require('express');
    var app = express();
    var fs = require('fs')
    app.get('/test', function (req, res) {
        fs.readFile('../index.html', function (err, data) {
            if (err) {
                res.status(500).send('read file1 error');
                console.log(res.status(500))
            }
            fs.readFile('../index.html', function (err, data) {
                if (err) {
                    res.status(500).send('read file2 error');
                }
                res.type('text/plain');
                res.send(data);
            });
        });
    });
    app.listen(3000,function(){
        console.log('Example app listening on port')
    })

web結果:
index.html的源碼輸出,不識別標籤

2. koa 2.0

随着新版Node.js开始支持ES6,Express的团队又基于ES6的generator重新编写了下一代web框架koa。

创建koa2工程

首先,我们创建一个目录hello-koa并作为工程目录用VS Code打开。然后,我们创建app.js,输入以下代码:

    // 导入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...');

web結果:


index4.png

对于每一个http请求,koa将调用我们传入的异步函数来处理:

    async (ctx, next) => {
        await next();
        // 设置response的Content-Type:
        ctx.response.type = 'text/html';
        // 设置response的内容:
        ctx.response.body = '<h1>Hello, koa2!</h1>';
    }

其中,参数ctx是由koa传入的封装了request和response的变量,我们可以通过它访问request和response,next是koa传入的将要处理的下一个异步函数。

上面的异步函数中,我们首先用await next();处理下一个异步函数,然后,设置response的Content-Type和内容。

由async标记的函数称为异步函数,在异步函数中,可以用await调用另一个异步函数,这两个关键字将在ES7中引入。

现在我们遇到第一个问题:koa这个包怎么装,app.js才能正常导入它?

\color{red}{方法一}:可以用npm命令直接安装koa。先打开命令提示符,务必把当前目录切换到hello-koa这个目录,然后执行命令:

C:\...\hello-koa> npm install koa@2.0.0

npm会把koa2以及koa2依赖的所有包全部安装到当前目录的node_modules目录下。

\color{red}{方法二}:在hello-koa这个目录下创建一个package.json,这个文件描述了我们的hello-koa工程会用到哪些包。完整的文件内容如下:

    {
        "name": "hello-koa2",
        "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"
        }
    }

其中,dependencies描述了我们的工程依赖的包以及版本号。其他字段均用来描述项目信息,可任意填写。

然后,我们在hello-koa目录下执行npm install就可以把所需包以及依赖包一次性全部装好:

C:...\hello-koa> npm install

很显然,第二个方法更靠谱,因为我们只要在package.json正确设置了依赖,npm就会把所有用到的包都装好。

注意,任何时候都可以直接删除整个node_modules目录,因为用npm install命令可以完整地重新下载所有依赖。并且,这个目录不应该被放入版本控制中。

koa middleware

让我们再仔细看看koa的执行逻辑。核心代码是:

app.use(async (ctx, next) => {
    await next();
    ctx.response.type = 'text/html';
    ctx.response.body = '<h1>Hello, koa2!</h1>';
});

每收到一个http请求,koa就会调用通过app.use()注册的async函数,并传入ctx和next参数。

我们可以对ctx操作,并设置返回内容。但是为什么要调用await next()?

原因是koa把很多async函数组成一个处理链,每个async函数都可以做一些自己的事情,然后用await next()来调用下一个async函数。我们把每个async函数称为middleware,这些middleware可以组合起来,完成很多有用的功能。

例如,可以用以下3个middleware组成处理链,依次打印日志,记录处理时间,输出HTML:

    const Koa = require('koa');
    const app = new Koa();
    app.use(async (ctx, next) => {
        console.log(555555555)
        console.log(`${ctx.request.method} ${ctx.request.url}`); // 打印URL
        await next(); // 调用下一个middleware
        console.log(7777777)
    });

    app.use(async (ctx, next) => {
        const start = new Date().getTime(); // 当前时间
        console.log(111111111)
        await next(); // 调用下一个middleware
        console.log(222222222)
        const ms = new Date().getTime() - start; // 耗费时间
        console.log(`Time: ${ms}ms`); // 打印耗费时间
    });

    app.use(async (ctx, next) => {
        console.log(333333333)
        await next();
        ctx.response.type = 'text/html';
        ctx.response.body = '<h1>Hello, koa2!</h1>';
        console.log(444444444)
    });
    app.listen(3000);
    console.log('http://127.0.0.1:3000/666')
index4.png

middleware的顺序很重要,也就是调用app.use()的顺序决定了middleware的顺序。

此外,如果一个middleware没有调用await next(),会怎么办?答案是后续的middleware将不再执行了。这种情况也很常见,例如,一个检测用户权限的middleware可以决定是否继续处理请求,还是直接返回403错误:

    var Koa = require('koa')
    var app = new Koa()
    function checkUserPermission(){
        console.log('wo')
    }
    app.use(async(ctx,next )=>{
        if(await checkUserPermission(ctx)){
            await next()
            console.log(666)
        }else{
            ctx.response.status = 403;
            console.log(777)
        }
    })
    app.listen(3000)
    console.log('http://127.0.0.1:3000')   
index4.png

理解了middleware,我们就已经会用koa了!

最后注意ctx对象有一些简写的方法,例如ctx.url相当于ctx.request.url,ctx.type相当于ctx.response.type。

小插曲:在安装之前,首先检查自己的所在目录的路径是否有中文字符,最好不要用中文字符,否则会有些莫名奇妙的报错,比如npm init生成package.json会有莫名的bug,或者安装插件的时候发错这样的error:

+ koa@2.0.0
added 38 packages from 20 contributors and audited 53 packages in 6.332s
found 1 high severity vulnerability
  run `npm audit fix` to fix them, or `npm audit` for details

按顺序的输入npm audit fix运行,在输入npm audit运行,就能debug这个errer,但是如果路径上有中文路径可能就debug不了。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,451评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,172评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,782评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,709评论 1 294
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,733评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,578评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,320评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,241评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,686评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,878评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,992评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,715评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,336评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,912评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,040评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,173评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,947评论 2 355

推荐阅读更多精彩内容

  • 一、历史 1、koa是express的下一代,是Express的团队基于ES6语法中的generator写的web...
    mjwz5294阅读 2,166评论 0 1
  • 参考资料 https://chenshenhai.github.io/koa2-note/note/static/...
    JunChow520阅读 10,486评论 1 8
  • 一、基本用法 1.1 架设 HTTP 服务 // demos/01.jsconst Koa = require('...
    majun00阅读 1,361评论 0 5
  • 本文由郝晨光整理总结并编写,未经允许禁止转载。 前言 学习koa,我之前学习过express,但是在使用expre...
    郝晨光阅读 1,250评论 0 12
  • Koa 必须使用 7.6 以上的版本。如果你的版本低于这个要求,就要先升级 Node。 基本用法 Koa 提供一个...
    Gukson666阅读 2,456评论 0 1