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 删除。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏大内老A

我的WCF之旅(3):在WCF中实现双工通信

双工(Duplex)模式的消息交换方式体现在消息交换过程中,参与的双方均可以向对方发送消息。基于双工MEP消息交换可以看成是多个基本模式下(比如请求-回复模式和...

1759
来自专栏Python中文社区

flask 项目搭建及配置分享

作者:Tom .Lee,GitHub ID:tomoncle ,Web and cloud computing developer, Java, Golang,...

1274
来自专栏数据和云

故障诊断 | 系统级追踪诊断方法及案例分享

所谓操作系统,是应用程序与服务器硬件进行沟通的中间层。应用程序的所有操作,都是和操作系统进行沟通交互。操作系统负责将所有交互转化为设备语言,进行硬件交互。 我们...

2993
来自专栏技术翻译

Elasticsearch文档和映射

在Elasticsearch的说法中,文档是序列化的JSON数据。在典型的ELK设置中,当您发送日志或度量标准时,它通常会发送到Logstash,Logstas...

461
来自专栏用户2442861的专栏

redis 学习指南

http://www.cnblogs.com/hoojo/p/4466024.html

641
来自专栏FreeBuf

如何利用Ptrace拦截和模拟Linux系统调用

ptrace(2)这个系统调用一般都跟调试离不开关系,它不仅是类Unix系统中本地调试器监控实现的主要机制,而且它还是strace系统调用常用的实现方法。ptr...

1257
来自专栏乐百川的学习频道

Python 日志输出

打印日志是很多程序的重要需求,良好的日志输出可以帮我们更方便的检测程序运行状态。Python标准库提供了logging模块,让我们也可以方便的在Python中打...

2049
来自专栏云计算

腾讯云支持 Terraform 开发实践

这篇文章从系统架构开始,到核心库讲解,到实践开发,再到单元测试,比较完整的描述了支持Terraform的开发全过程。

2.7K16
来自专栏同步博客

掀开断点续传那一层面纱(下载篇)

  这一篇文章主要介绍的是http协议下载时的断点续传,详细到各个步骤。主要步骤有:DNS查找、TCP三次握手、http请求发送、TCP协议数据传输、暂停后的状...

974
来自专栏pangguoming

AngularJS中的按需加载ocLazyLoad

初学者,有不足的地方希望各位指出 一、前言     ocLoayLoad是AngularJS的模块按需加载器。一般在小型项目里,首次加载页面就下载好所有的资源...

3428

扫码关注云+社区