個人博客開發系列文章:
- 博客前端展示總結:http://www.lxweimin.com/p/1348bcd1e716
- 后臺管理系統總結:http://www.lxweimin.com/p/53c75476be44
- 服務端總結:http://www.lxweimin.com/p/c25b5432d6f5
- Travis CI持續集成:http://www.lxweimin.com/p/4e7a06e18bd5
- 使用Nginx配置HTTPS和反向代理:http://www.lxweimin.com/p/c779e54c9f85
全棧開發—博客服務端(Koa2)
- 博客地址:shirmy
- 項目地址:smile-blog-koa
主要技術
- 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
中,步驟如下:
- 安裝
jsonwebtoken
插件 - 給用戶頒發一個由用戶id、用戶權限級別、超時時間生成的
accessToken
- 客戶端把
accessToken
保存到cookie
中,然后以后的每次發送請求都會攜帶這個token
- 使用koa中間件,在API處理前校驗
token
是否合法,并且判斷用戶是否有權限訪問該API
其它業務代碼及框架的基本用法就不多說了,可以直接參考smile-blog-koa
參考文檔