二、用koa库进行web开发

一、历史

1、koa是express的下一代,是Express的团队基于ES6语法中的generator写的web框架

2、express 的劣势:它是基于ES5的语法,如果异步嵌套层次过多,代码写起来就非常难看

(1)这是用express的代码:

app.get('/test', function (req, res) {

    fs.readFile('/file1', function (err, data) {

        if (err) {

            res.status(500).send('read file1 error');

        }

        fs.readFile('/file2', function (err, data) {

            if (err) {

                res.status(500).send('read file2 error');

            }

            res.type('text/plain');

            res.send(data);

        });

    });

});

(2)使用koa:将读取文件的方法移出去

app.use('/test', function *() {

    yield doReadFile1();

    var data = yield doReadFile2();

    this.body = data;

});

(3)为了简化异步代码,ES7(目前是草案,还没有发布)引入了新的关键字async和await,可以轻松地把一个function变为异步模式。koa团队非常超前地基于ES7开发了koa2,它完全使用Promise并配合async来实现异步:

app.use(async (ctx, next) => {

    await next();

    var data = await doReadFile();

    ctx.response.type = 'text/plain';

    ctx.response.body = data;

});

出于兼容性考虑,目前koa2仍支持generator的写法,但下一个版本将会去掉。

二、入门应用

1、源码:

// 导入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 = '

Hello, koa2!

';

});

// 在端口3000监听:

app.listen(3000);

console.log('app started at port 3000...');

(1)ctx:封装了request和response的变量

(2)await调用另一个异步函数

2、导入koa2:编辑好package.json中的依赖项,然后执行npm install

3、启动方式:根据喜好选择

(1)使用终端:node app.js

(2)VSCode:编辑好launch.json,然后使用VSCode的调试工具

(3)npm:编辑package.json中的scripts,其中start对应的就是启动命令

4、middleware:koa中的关键概念

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

(2)koa可以把很多async函数组成一个处理链,每个async函数都可以做一些自己的事情,然后用await next()来调用下一个async函数。我们把每个async函数称为middleware,这些middleware可以组合起来,完成很多有用的功能。例如,可以用以下3个middleware组成处理链,依次打印日志,记录处理时间,输出HTML:

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 = '

Hello, koa2!

';

});

(3)调用app.use()的顺序决定了middleware的顺序,如果一个middleware没有调用await next(),后续的middleware将不再执行了。通过这样的机制,我们可以控制程序执行的走向。如:

app.use(async (ctx, next) => {

    if (await checkUserPermission(ctx)) {

        await next();

    } else {

        ctx.response.status = 403;

    }

});

三、处理URL

1、引入koa-router这个middleware,让它负责URL映射的各个处理方式。

(1)先用npm导入koa-router

(2)使用koa-router之前:

app.use(async (ctx, next) => {

    if (ctx.request.path === '/test') {

        ctx.response.body = 'TEST page';

    } else {

        await next();

    }

});

(3)使用koa-router之后:

router.get('/hello/:name', async (ctx, next) => {

    var name = ctx.params.name;

    ctx.response.body = `

Hello, ${name}!

`;

});

router.get('/', async (ctx, next) => {

    ctx.response.body = '

Index

';

});

// add router middleware:

app.use(router.routes());

相当于把URL映射交给了koa-router,最终把koa-router交给app,并省略了await语句,非常简洁。

2、koa-bodyparser用于解析原始request请求,然后,把解析后的参数,绑定到ctx.request.body中,这样我们也能处理post请求了。

const bodyParser = require('koa-bodyparser');

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 = `

Welcome, ${name}!

`;

    } else {

        ctx.response.body = `

Login failed!

       

Try again

`;

    }

});

3、有了处理URL的办法,我们可以把项目结构设计的更合理:

url2-koa/

|

+- .vscode/

|  |

|  +- launch.json <-- VSCode 配置文件

|

+- controllers/

|  |

|  +- login.js <-- 处理login相关URL

|  |

|  +- users.js <-- 处理用户管理相关URL

|

+- app.js <-- 使用koa的js

|

+- package.json <-- 项目描述文件

|

+- node_modules/ <-- npm安装的所有依赖包

(1)把不同URL映射任务分别组织到不同的moudles中,把他们放在controllers目录下面

var fn_hello = async (ctx, next) => {

    var name = ctx.params.name;

    ctx.response.body = `

Hello, ${name}!

`;

};

module.exports = {

    'GET /hello/:name': fn_hello

};

(2)添加controller.js模块,让它自动扫描controllers目录,找到所有js文件,导入,然后注册每个URL

function addMapping(router, mapping) {//根据映射模块执行映射任务

    for (var url in mapping) {

        if (url.startsWith('GET ')) {

            var path = url.substring(4);

            router.get(path, mapping[url]);

            console.log(`register URL mapping: GET ${path}`);

        } else if (url.startsWith('POST ')) {

            var path = url.substring(5);

            router.post(path, mapping[url]);

            console.log(`register URL mapping: POST ${path}`);

        } else {

            console.log(`invalid URL: ${url}`);

        }

    }

}

function addControllers(router) { //导入映射模块

    var files = fs.readdirSync(__dirname + '/controllers');

    var js_files = files.filter((f) => {

        return f.endsWith('.js');

    });

    for (var f of js_files) {

        console.log(`process controller: ${f}...`);

        let mapping = require(__dirname + '/controllers/' + f);

        addMapping(router, mapping);

    }

}

module.exports = function (dir) {

    let

        controllers_dir = dir || 'controllers', // 如果不传参数,扫描目录默认为'controllers'

        router = require('koa-router')();

    addControllers(router, controllers_dir);

    return router.routes();

};

(3)简化app.js,将来app.js就不会与‘URL映射任务’耦合了

// 导入controller middleware:

const controller = require('./controller');

// 使用middleware:

app.use(controller());

四、Nunjucks:一个模板引擎,说白了就是拼字符串,http://mozilla.github.io/nunjucks/,这部分不深入学习

1、一般的应用,就是拼接一个html,返回给客户端。

2、结合面向对象、URL处理,我们很容易想到MVC模式。

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

推荐阅读更多精彩内容