本文是对之前 KOA2系列 文章的一次总结、升级、优化, 也是基于 TypeScript 对之前 Web 框架的重写.
本文介绍的 KOA Web 框架特点:
- 基于 JavaScript 超集 TypeScript 重写
- Web Session 存储于 redis 缓存
- 引入 ORM 框架 node-orm2 管理 models 对象, 本文采用 mysql 数据库
- 引入数据库迁移管理工具 migrate-orm2
- 参考 Django 路径设计思路, 重新设计文件目录
- 使用 PM2 管理守护 Web 进程
环境要求
1、Node 环境 node >= 7.6 (需要原生的 async/await 语法支持)
2、推荐采用 yarn 管理依赖, 使用npm 也可以
3、TypeScript >= 2.x
4、PM2 安装配置
- 全局安装 PM2: npm install -g pm2
- 安装 PM2 的 TypeScript 解释器: pm2 install typescript (执行该命令建议翻墙)
5、获得 mysql 数据库配置参数
6、获得 redis 缓存配置参数
使用该框架
框架 git 仓库地址: https://github.com/FeifeiyuM/koa2-typescript
1、拉取框架代码 git clone git@github.com:FeifeiyuM/koa2-typescript.git
2、安装依赖: yarn install ( npm install )
3、添加 mysql & redis 配置参数, (如何修改在后面介绍)
4、启动服务:
- 开发环境启动服务: 在工程根目录下执行命令: npm start
- 生产环境部署服务: 在工程跟目录下执行命令: npm run deploy
5、数据库更新迁移:(需要编写更新文件)
- 执行数据库迁移命令: npm run migrate
6、删除开启的所有 PM2 进程
- 执行删除进程命令: npm run del
工程目录结构
|
|
config 配置信息
1、env.config.js 文件
该文件中主要用于编辑系统相关的配置信息,例如数据库连接,开放端口号等信息
该文件主要配置信息主要分为两部分,一部分是开发环境的配置参数, 另一部分是生成环境的配置数据
123456789101112131415161718192021222324252627282930313233343536373839404142434445 //判断当前环境类型const isProdEnv:boolean = process.env.NODE_ENV === 'production'//开发环境下的配置参数//listen portlet listenPort:Number = 3000//Redis Configlet redis = {port: 6379,host: '192.168.1.100',db: 8}//Mysql Configlet mysql = {host: '192.168.1.100',database: 'nodeweb',user: 'root',password: 'mysql123',protocol: 'mysql',port: '3306'}//生产环境下的配置参数if(isProdEnv) { //覆盖开发环境的参数listenPort = 3000redis = {port: 6379,host: '192.168.1.100',db: 9}mysql = {host: '192.168.1.100',database: 'nodeweb',user: 'root',password: 'mysql123',protocol: 'mysql',port: '3306'}}//模块导出export default {listenPort: listenPort,redis: redis,mysql: mysql}
2、PM2 启动配置文件
PM2 启动文件分为开发、生产两个, 两个文件大致相同
name: PM2指进程名; script: 入口执行文件,即 app.ts;
log_file: 指日志文件,PM2 会自动搜集系统输出日志,并输出至 logs 目录下的 app.log 文件
error_file: error及以上级别日志, 输出至 logs 目录下的 err.log 文件
PM2 配置编写说明
12345678910111213141516171819202122232425262728293031323334353637 {"apps" : [{"name": "devenv","script": "./src/app.ts","instances": 1,"exec_mode": "fork","watch": true,"ignore_watch" : ["node_modules", "public", "logs", "views", "package.json", "config", ".git/*"],"out_file": "./logs/app.log","error_file": "./logs/err.log","log_date_format" : "YYYY-MM-DD HH:mm Z","combine_logs": true,"listen_timeout": 8000,"kill_timeout": 1600,"env": {"NODE_ENV": "development"}}]}{"apps" : [{"name": "prodenv","script": "./src/app.ts","instances": 1,"exec_mode": "fork","watch": false,"out_file": "./logs/app.log","error_file": "./logs/err.log","log_date_format" : "YYYY-MM-DD HH:mm Z","combine_logs": true,"listen_timeout": 8000,"kill_timeout": 1600,"env": {"NODE_ENV": "production"}}]}
TypeScript 配置
TypeScript 配置文件 tsconfig.json tsconfig 配置
系统入口文件 app.ts
src 目录下的 app.ts 文件与之前KOA2系列文章中介绍的大致相当, 不作详细介绍
注意: 模块引入时, 有些模块不能直接采用 import Koa from ‘koa’, 需要改成: import * as Koa from ‘koa’
utils 模块介绍
1、session.ts: 参考 koa2-web-Session
2、redis.ts: 参考 koa2-web-数据持久化
3、orm.ts: 参考 koa2-web-数据持久化, node-orm2 具体 model 配置将在后面介绍
4、auth.ts: 用户认证模块,这个是 koa-router 的中间件函数, 在路由中执行
base 路径下文件介绍
1、该目录下需要介绍是 models.js 文件,这个文件主要是用来初始化其他业务的 model
2、api.ts, router.ts, test.ts 用法与其他业务模块的类似,在后面介绍
业务模块 (Account为例)
1、数据建模( models.ts ), 将数据库中的某一张表,对应映射成一个数据对象。
这一块主要涉及 node-orm2 的使用, 详细文档可以参考 node-orm2 wiki
主要设计的知识点有 node-orm2 wiki 中的 Defining Models, Defining Associations
以用户表为例:
123456789101112131415161718192021222324252627282930313233343536373839404142 import * as orm from 'orm'export const User = (db):void => { //以函数形式导出, 给上面介绍的 InitModels() 调用const user = db.define('user', { // 'user' 为表名, 对应数据库中的 user 表// id 字段, 对应 user 表中的 id 字段,//{ }, 中描述的是该字段的属性, 具体查看 node-orm2 wiki 中的 Model Propertiesid: {type: 'serial', key: true},password: {type: 'text', size: 128, required: true}, // user 表中的 password 字段type: {type: 'integer', size: 0|1|2, defaultValue: 0},nick: {type: 'text', required: true},mobile: {type: 'text', required: true},last_login: {type: 'date', time: true},create_time: {type: 'date', time: true, required: true},update_time: {type: 'date', time: true, required: true}}, {validations: { //提交字段校验 具体查看 node-orm2 wiki 中的 Model Validationsnick: [orm.enforce.unique('nick has been used'), //唯一性校验orm.enforce.ranges.length(5, undefined, 'nick must be at least 5 letters long'),orm.enforce.ranges.length(undefined, 30, 'nick can not be longer than 30 letters') //长度校验],mobile: [orm.enforce.unique('this mobile has be registed'), //唯一性校验orm.enforce.ranges.length(11, 11, 'mobile number length must be 11')]},hooks: { //事件钩子, 定义了数据存取的各个阶段, 具体查看 node-orm2 wiki 中的 Model HooksbeforeValidation(next) {this.create_time = new Date().toISOString().slice(0, 19)return next()}},methods: { //序列化输出, 为 user 对象定义一个 baseInfo 方法,获取对应字段baseInfo() {return {nick: this.nick,mobile: this.mobile,type: this.type}}}})}
2、定义接口(api.ts), 将主要的业务逻辑放在 api.ts 中实现, 包括 数据存取,更新, 相关业务逻辑
以 Account 下 api.ts 为例:主要实现用户创建和用户信息读取的功能
需要注意的点:
- 在 api.ts 中导入的是 utils 中的 InitModels 方法
- api 类中必须定义一个init()方法, 用于初始化整个 model,
- api 中所有的方法返回的都为 Promise 对象
- api 中主要用到的是数据库 增,删,改,查功能, 涉及的知识点有 node-orm2 wiki 中的 Finding Items, Creating and Updating Items, Aggregation
|
|
3、路由编写 (router.ts), 实现页面渲染 或 http 接口
路由编写同之前的 KOA2系列 中的 REST 接口编写类似
本文中的添加的内容是 koa-router 中间件, 以实现用户登入校验
注意: 每个 router.ts 都要添加 model 初始化的代码, 主要为了保证在接口调用前 model 都已初始化
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162 import * as Router from 'koa-router'import * as Log4js from 'koa-log4'import Account from './api' //导入之前编写的 api 类import { userAuth } from '../utils/auth' //导入用户认证函数import { errAnal } from '../utils/error' //异常处理函数const router = new Router()let account:any = null // 全局存储 account 对象const logger = Log4js.getLogger('account')//该函数是初始化 model 中间件const initAccount = async (ctx, next) => {if(!account) { //如果 account 对象为定义, 保证该对象只会初始化一次account = new Account()try {await account.init() //等待 account 初始化成功, 本质上是等待整个 model 初始化成功} catch(err) {logger.error('account init failed')}await next()} else { //如果已经初始化的话,直接下一步await next()}}//当有请求进入时,先检查 account 是否已经初始化//这部分代码同 api 中 init()方法配合使用, 时必须添加的router.use(initAccount)router.prefix('/account') //给当前文件中的 所有路由都加个 account 前缀//如下 /account/info/:id 这个接口访问需要登入//在进入到逻辑处理函数前,先添加 userAuth 函数验证用户//如果验证通过 则进入到之后的逻辑处理函数,//如果处理失败, userAuth 函数中会直接返回错误router.get('/info/:id', userAuth, async(ctx) => {let id = Number(ctx.params.id)let userInfo:any = nulltry {userInfo = await account.getById(id)} catch(err) {logger.error(err)}ctx.body = JSON.stringify(userInfo)})// 如下: /account/register 这个接口不需要用户验证, 则不用添加 userAuth 方法router.post('/register', async (ctx) => {let nick:string = ctx.request.body.nicklet mobile:string = ctx.request.body.mobilelet password:string = ctx.request.body.passwordlet type:number = Number(ctx.request.body.type)let user = {nick: nick,mobile: mobile,password: password,type: type}let result:any = nulltry {result = await account.createUser(user)ctx.body = JSON.stringify(result)} catch(err) {logger.error('in error', err)errAnal(ctx, err)}})export default router
4、 test.ts 用于编写测试脚本
数据库迁移 migration
本框架中采用的数据库迁移工具是 migrate-orm2
执行命令: npm run migrate 会执行 migrations/index.ts 脚本, 根据脚本中的配置会相应调用那些数据库更新脚本,(例如: 001-test.js)
注意:
- migrate-orm2 工具目前(2017/03/07)无法实现修改字段名称
- dropColumn(或 drop相关操作)请谨慎使用, 有时会将整个表给删除
package.json 模块
package.json 中主要介绍系统的启动命令, 在 script 中编写
npm start: 按开发环境配置启动整个工程, 并输出日志
npm run deploy: 按生产环境配置启动整个工程,
npm run migrate: 执行数据库迁移脚本
npm run del: 删除所有 PM2 正在运行的进程
123456 "scripts": {"start": "pm2 start config/pm2.dev.config.json && pm2 logs","deploy": "pm2 start config/pm2.prod.config.json","migrate": "pm2 start migrations/index.ts && pm2 logs","del": "pm2 delete all"}