全栈开发—博客服务端(Koa2)

个人博客开发系列文章:

全栈开发—博客服务端(Koa2)

主要技术

  • Koa2
  • MySQL
  • Sequelize
  • JWT
  • Axios
  • Validator.js

项目特点

  • 封装权限控制中间件
  • 清晰的项目结构
  • 简洁易用的参数校验、异常处理
  • 支持用户无感知刷新

技术总结

项目结构

├── app                     # 业务代码
│   ├── api                 # api
│   │   ├── blog            # 提供给博客前端API
│   │   └── v1              # 提供给博客管理系统API
│   ├── dao                 # 数据库操作层
│   ├── lib                 # 工具函数、工具类、常量
│   ├── models              # Sequelize model 层
│   └── validators          # 参数校验工具类
├── config                  # 全局项目配置
├── core                    # 核心库
│   ├── db.js               # Sequelize 全局配置
│   ├── http-exception.js   # 异常处理定义
│   ├── init.js             # 项目初始化
│   ├── lin-validator.js    # 参数校验插件
│   ├── multipart.js        # 文件上传处理
│   └── util.js             # 核心库工具函数
├── middleware              # 中间件

导入模块太多?

Koa2写服务端代码,有一个体验就是文件导出来导出去,各种路径,这时我们可以使用别名:

npm install -S nodule-alias
{
  // ...
  "_moduleAliases": {
    "@models": "app/models",
  }
}
// app.js
require('module-alias/register')

// article.js
const { Article } = require('@models')

自动注册路由中间件

当路由模块很多时,在app.js中一个个导入岂不是越写越长,这时我们可以借助require-directory工具

const requireDirectory = require('require-directory')
const Router = require('koa-router')

class InitManager {
  static initCore(app) {
    // 入口
    InitManager.app = app
    InitManager.initLoadRoutes()
  }

  static initLoadRoutes() {
    // process.cwd() 获取绝对路径
    const appDirectory = `${process.cwd()}/app/api`
    // 使用 require-directory 提供的方法导入自动导入路由文件
    requireDirectory(module, appDirectory, {
      visit: whenLoadingModule
    })

    // 注册所有检测到的 Koa 路由
    function whenLoadingModule(obj) {
      if (obj instanceof Router) {
        InitManager.app.use(obj.routes())
      }
    }
  }
}

module.exports = InitManager

// app.js
const Koa = require('koa')
const app = new Koa()

InitManager.initCore(app)

如何使用七牛云上传?

实际上官方文档就已经写得很清楚了:Node.js SDKV6,无非就是安装插件,照着文档搬运代码。

在这里要注意的是,如果上传多个文件,我们需要放在一个循环里逐一上传,而上传又是异步的,那么如何验证所有文件都已经上传成功,在这里我们可以使用Promise.all()方法进行封装,举个栗子:

class UpLoader {
  async upload(files) {
    let promise = []
    
    for (const file of files) {
      // ...
      promise.push(new Promise((resolve, reject) => {
        // 执行上传逻辑
        // resolve() or reject()
      }))
    }

    Promise.all(promises).then(res => {
      // ... 全部成功
    }).catch(e => {
      // ... 有上传失败的
    })
  }
}

全局异常捕获中间件

// http-exception.js
class HttpException extends Error {
  constructor(msg = '服务器异常', errorCode = 10000, code = 400) {
    super()
    this.msg = msg
    this.errorCode = errorCode
    this.code = code
  }
}

// exception.js
const { HttpException } = require('@exception')

const catchError = async (ctx, next) => {
  try {
    // 利用洋葱圈模型的特性,所有请求都会经过这里
    await next()
  } catch (error) {
    const isHttpException = error instanceof HttpException
    const isDev = global.config.environment = 'dev'
    if (isDev && !isHttpException) {
      throw error
    }
    // 已知错误
    if (isHttpException) {
      ctx.body = {
        msg: error.msg,
        errorCode: error.errorCode,
        request: `${ctx.method}: ${ctx.path}`
      }
      ctx.status = error.code
    } else {
      // 未知错误
      ctx.body = {
        msg: '服务器内部错误',
        errorCode: 999,
        request: `${ctx.method}: ${ctx.path}`
      }
      ctx.status = 500
    }
  }
}

module.exports = catchError

// app.js
const catchError = require('./middleware/exception')

app.use(catchError)

如何进行权限校验

权限校验是通过JWT实现的,使用JWT可以用用户ID、超时时间、权限级别给用户生成一个Token返回到客户端,客户端再把这个Token存储到cookie中,步骤如下:

  1. 安装jsonwebtoken插件
  2. 给用户颁发一个由用户id、用户权限级别、超时时间生成的accessToken
  3. 客户端把accessToken保存到cookie中,然后以后的每次发送请求都会携带这个token
  4. 使用koa中间件,在API处理前校验token是否合法,并且判断用户是否有权限访问该API

其它业务代码及框架的基本用法就不多说了,可以直接参考smile-blog-koa

参考文档

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

推荐阅读更多精彩内容

  • 一、背景 Koa 是一个新的 web 框架,由 Express 幕后的原班人马打造, 致力于成为 web 应用和 ...
    bayi_lzp阅读 10,213评论 6 25
  • 下午和一位好友聊天的时候,她不止一次的提到,和你聊天太有收获了,我们有多久没有这样聊过天了呀。 是啊,我也在问自己...
    乍暖还寒的港湾阅读 462评论 0 1
  • 这些日子,简书就像日记本似的出现在我的生活中。说起来我的写作日不止21天,在此之前挑战过两次日更,终在不到10天的...
    宁檬Lemoon阅读 151评论 0 0