Wafer2 Node.js QuickStart 架构分析

与 Wafer1 的 Node.js Demo 采用 Express 不同,Wafer2 的 Node.js QuickStart 采用了 Koa.js 框架编写,Koa 将整个请求过程看做全异步的操作,使用 Node.js 7.6 开始支持的 async/await 语法,大大简化了整个应用开发的繁琐性,能让我们写出更加好看的异步代码。关于 async/await,这里不过多的介绍,有兴趣的同学可以查看阮一峰的 async/await 教程

Koa 中间件 - 洋葱模型

Koa 的中间件模式和 Express 不同,Express 的中间件是流式串行结构,当一个中间件调用了 next 函数,逻辑就再也不会回到这个中间件中,这种模型在一些情况下会使得很多我们想要实现的功能变得复杂,比如请求时间计算,我们不得不将请求的开始时间写进 request 对象,而对于全局错误的捕获,我们也只能监听 uncaughtException 事件,这样导致我们的应用架构变得不够清晰。

而 Koa 的洋葱模型巧妙的解决了这个文件,它将所有的中间件(或者处理业务的函数)看成是异步的,next 函数则返回一个 Promise 对象,每一个中间件会包裹住下一个中间件,如同洋葱一样,请求(ctx 对象)从最外层进到最里层,又从最里层再流出来,简化了我们整个应用设计的难度。

response 中间件

同大部分 Node.js 程序一样,应用的入口是 app.js,应用的最开始会先引入一个 response 中间件:

app.use(response)

这个中间件用来处理整个应用的异常捕获和请求响应结束之后的响应数据封装。

打开 middlewares/response.js 文件,可以看到如下代码:

const debug = require('debug')('koa-weapp-demo')

/**
 * 响应处理模块
 */
module.exports = async function (ctx, next) {
    try {
        // 调用下一个 middleware
        await next()

        // 处理响应结果
        // 如果直接写入在 body 中,则不作处理
        // 如果写在 ctx.body 为空,则使用 state 作为响应
        ctx.body = ctx.body ? ctx.body : {
            code: ctx.state.code !== undefined ? ctx.state.code : 0,
            data: ctx.state.data !== undefined ? ctx.state.data : {}
        }
    } catch (e) {
        // catch 住全局的错误信息
        debug('Catch Error: %o', e)

        // 设置状态码为 200 - 服务端错误
        ctx.status = 200

        // 输出详细的错误信息
        ctx.body = {
            code: -1,
            error: e && e.message ? e.message : e.toString()
        }
    }
}

response 中间件对全局做了一个 try...catch 操作,将 next() 的调用错误给 catch 住,防止请求过程中的某一个错误导致整个 Node 程序退出。同时,在请求结束之后,会从 ctx.state 取出 data 和 code 两个字段,封装进 ctx.body 中,ctx.body 就是整个引用最后返回给前端的数据,Koa 会对 ctx.body 进行 JSON 序列化,并在 http header 上加上响应的类型头。

路由

QuickStart 使用的是 koa-router 来处理路由映射,打开 routes/index.js 可以看到,本文件中统一处理所有的路由。其中与用户登录授权有关的两个路由与别的不同,分别使用了 SDK 导出的 authorizationMiddleware 和 validationMiddleware 中间件。

// --- 登录与授权 Demo --- //

// 登录接口
router.get('/login', authorizationMiddleware, controllers.login)

// 用户信息接口(可以用来验证登录态
router.get('/user', validationMiddleware, controllers.user)

只有通过了这两个中间件的处理,才会执行后面相关的 controllers。

控制器映射

QuickStart 中还有一个值得分享的就是控制器映射,打开 controllers/index.js 文件,可以看到如下代码:

const _ = require('lodash')
const fs = require('fs')
const path = require('path')

/**
 * 映射 d 文件夹下的文件为模块
 */
const mapDir = d => {
    const tree = {}

    // 获得当前文件夹下的所有的文件夹和文件
    const [dirs, files] = _(fs.readdirSync(d)).partition(p => fs.statSync(path.join(d, p)).isDirectory())

    // 映射文件夹
    dirs.forEach(dir => {
        tree[dir] = mapDir(path.join(d, dir))
    })

    // 映射文件
    files.forEach(file => {
        if (path.extname(file) === '.js') {
            tree[path.basename(file, '.js')] = require(path.join(d, file))
        }
    })

    return tree
}

// 默认导出当前文件夹下的映射
module.exports = mapDir(path.join(__dirname))

controllers/index.js 是所有控制器默认导出的文件,他遍历了 controllers 文件夹下的所有文件和文件夹,并生成一个对象结构。

例如,如下的文件夹结构:

── controllers
   ├── user.js
       └── login.js
   └── upload.js

会被映射为如下的 JavaScript 对象:

{
    user: {
        login: require('./user/login.js')
    },
    upload: require('./upload.js')
}

这样,我们只需引入 controllers/index.js 就可以引入所有的 controllers,创建一个新的文件夹和文件,也可以马上通过 controllers/index.js 来访问到,无需单独 require,让整个应用更加的清晰。

结语

这次关于 Node.js 版本 QuickStart 的代码就分享到这里,欢迎大家使用腾讯云微信小程序解决方案。如果有什么更好的意见或者建议,可以在评论中提出来,一起讨论一下。

原创声明,本文系作者授权云+社区-专栏发表,未经许可,不得转载。

如有侵权,请联系 yunjia_community@tencent.com 删除。

发表于

我来说两句

4 条评论
登录 后参与评论

相关文章

来自专栏Python爱好者

Java基础笔记19

1296
来自专栏逸鹏说道

C#线程篇---让你知道什么是线程(1)

线程线程,进程进程,到底什么是线程,什么是熟练多线程编程? 今天来和大家一起讨论讨论线程基础,让大家知道线程的基本构造。 说线程之前,先要了解下进程,这个可不能...

3489
来自专栏IT技术精选文摘

史上最全Web端即时通讯技术原理详解

822
来自专栏用户2442861的专栏

Python 关于 name main的使用 if __name__ == '__main__':

这段代码的主要作用主要是让该python文件既可以独立运行,也可以当做模块导入到其他文件。当导入到其他的脚本文件的时候,此时__name__的名字其实是导入模块...

541
来自专栏LuckQI

学习Java基础知识,打通面试关十一~文件的拷贝

954
来自专栏haifeiWu与他朋友们的专栏

Java命令之javap初探

javap是jdk自带的一个工具在jdk安装目录的/bin下面可以找到,可以对代码反编译,也可以查看java编译器生成的字节码,对代码的执行过程进行分析,了解j...

582
来自专栏IT技术精选文摘

Nginx架构初探(值得细品的长篇好文)

众所周知,nginx性能高,而nginx的高性能与其架构是分不开的。那么nginx究竟是怎么样的呢?这一节我们先来初识一下nginx框架吧。 nginx在启动后...

2186
来自专栏炸天帮5

win32进程概念之句柄表,以及内核对象.

我们知道.我们使用CreateProcess 的时候会返回一个进程句柄.以及线程句柄. 其实在调用CreateProcess的时候.内核中会新建一个EPROCE...

151
来自专栏落影的专栏

静态库与动态库的思考

前言 在上文《编译与链接过程的思考》评论中暴走大牙提到了静态库和动态库依赖的问题,还在群里提了几个测试样例和测试工程。 大致介绍下测试工程和如何进行测试: ...

3476
来自专栏iKcamp

走进Node.js 之 HTTP实现分析

作者:正龙(沪江Web前端开发工程师) 本文为原创文章,转载请注明作者及出处 上文“走进Node.js启动过程”中我们算是成功入门了。既然Node.js的强...

2426

扫码关注云+社区