时隔 8 个月,经历一番 chuozhe,我又回来了...
之前一直用 node 的 express,没有太多框架封装的东西,需要自己手撸 sql 语句,配合 mysql 依赖包进行数据库的连接会话及读写操作。但框架封装的东西少了,自己做的就多了,最近想写一个不知名的 proj,为了进一步减少我写 demo 时的工作量,我决定换个框架试试。
koa2 是由 Express 原班人马打造的,致力于成为一个更小、更富有表现力、更健壮的 Web 框架。 使用 koa 编写 web 应用,通过组合不同的 generator,可以免除重复繁琐的回调函数嵌套, 并极大地提升错误处理的效率。koa 不在内核方法中绑定任何中间件, 它仅仅提供了一个轻量优雅的函数库,使得编写 Web 应用变得得心应手。
const Koa = require('koa') const router = require('koa-router')() //注意:引入的方式 const app = new Koa() router.get('/user/find', function (ctx, next) { const { username, sex } = ctx.request.query // 从上下文的 request 对象中获取 ctx.body = '获取所有用户...' })
const Koa = require('koa') const router = require('koa-router')() //注意:引入的方式 const bodyParser = require('koa-bodyparser') const app = new Koa() // 对于POST请求的处理,koa-bodyparser中间件可以把koa2上下文的formData数据解析到ctx.request.body中 app.use(bodyParser()) router.post('/user/add', function (ctx, next) { const { username, sex, age } = ctx.request.body // 从上下文的 request 对象中获取 ctx.body = '添加用户...' })
中间件就是匹配路由之前或者匹配路由完成做的一系列的操作,我们就可以把它叫做中间件。
在 express 中间件(Middleware) 是一个函数,它可以访问请求对象(request object (req)), 响应对象(response object (res)), 和 web 应用中处理请求-响应循环流程中的中间件,一般被命名为 next 的变量。在 Koa 中中间件和 express 有点类似。
const Koa = require('koa') const app = new Koa() // 01 应用级中间件 app.use(async (ctx, next) => { console.log(`Process ${ctx.request.method} ${ctx.request.url}...`) await next() }) // 02 路由中间件 const Router = require('koa-router') const router = new Router() router.get('/', async (ctx, next) => { console.log(1) ctx.bosy = 'hello koa ~' }) // 03 错误处理中间件 app.use(async (ctx, next) => { next() if (ctx.status === 500) { ctx.status = 500 ctx.body = '服务器出了点小意外~' } }) // 04 第三方中间件 const bodyParser = require('koa-bodyparser') app.use(bodyParser()) // 处理 body 中的参数
// 例如在以下的入口 app.js 中测试以下中间件(已省略无关代码) ... const Koa = require("koa") const app = new Koa() // 中间件 01 app.use(async (ctx, next) => { console.log('->>>', 1) await next() console.log('<<<-', 111) }) // 中间件 02 app.use(async (ctx, next) => { console.log('->>>', 2) await next() console.log('<<<-', 222) }) // 中间件 03 app.use(async (ctx, next) => { console.log('->>>', 3) await next() console.log('<<<-', 333) }) app.listen(3000) ... // 输出: ->>> 1 ->>> 2 ->>> 3 <<<- 333 <<<- 222 <<<- 111
在以上测试伪代码中,每个中间件都接收了一个 next 参数,在 next 函数运行之前的中间件代码会在一开始就执行,next 函数之后的代码会在内部的中间件全部运行结束之后才执行:
它首先会走中间件 01,进而打印 ->>> 1
,继续走碰到 next,会进入下一个中间件 02,进而打印出 ->>> 2
,继续走碰到 next,会进入下一个中间件 03,进而打印出 ->>> 3
。依此类推,当走到最后一个中间件后因为没有下一个中间件可执行,从最后一个中间件一层一层返回执行,这里假如 03 是最后一个中间件。继续走,它会打印出 <<<- 333
,中间件 03 执行完毕,返回上一个中间件 02,打印出 <<<- 222
,中间件 02 执行完毕,返回上一个中间件 01,打印出 <<<- 111
,至此所有中间件执行完毕。而这里中间件的执行过程就是 洋葱圈模型
。
koa-swagger-decorator 可以自动生成 swagger json 文档,让我们可以表面使用 MVC 式的注解式路由方式来撸接口及文档。
// npm install --save-dev babel-plugin-transform-decorators-legacy // .babelrc { "presets": [["env", { "targets": { "node": "current" } }]], "plugins": ["transform-decorators-legacy"] }
// SwaggerRouter.js import { SwaggerRouter } from 'koa-swagger-decorator' import * as path from 'path' const router = new SwaggerRouter() // swagger 文档地址: http://localhost:3000/api/swagger-html router.swagger({ title: 'A project', description: 'Api doc', version: '1.0.0', }) // 查找对应目录下的controller类: 会将 controller 文件夹下的注解式接口生成一个个的 router router.mapDir(path.resolve(__dirname, '../controller/')) export default router // app.js import router from './router/SwaggerRouter' app.use(router.routes())
// UserController.js import { request, summary, description, // 接口名称下方的描述信息 query, // get时参数 path, // post, put, delete 时地址栏参数 body, // body中的参数 tags, } from 'koa-swagger-decorator' // 引入我的业务操作 import UserService from '../service/UserService' const userService = new UserService() const tag = tags(['User']) export default class UserController { @request('post', '/user/findById') @summary('根据id查询用户数据') @tag @body({ id: { type: 'string', required: true }, }) async findById(ctx) { const bObj = ctx.request.body const data = await userService.findById(bObj) ctx.rest(data) } }
sequelize 是一个基于 promise 的 Node.js ORM, 目前支持 Postgres, MySQL, MariaDB, SQLite 以及 Microsoft SQL Server. 它具有强大的事务支持, 关联关系, 预读和延迟加载,读取复制等功能。
$ npm install --save sequelize # 必须手动为所选数据库安装驱动程序: 选择以下之一: $ npm install --save pg pg-hstore # Postgres $ npm install --save mysql2 $ npm install --save mariadb $ npm install --save sqlite3 $ npm install --save tedious # Microsoft SQL Server
更多 Sequelize 构造函数参数参考 API
// sequelizeMysql.js import Sequelize from 'sequelize' const DBConfig = { host: 'localhost', // 服务器地址 port: 3306, // 数据库端口号 username: 'root', // 数据库用户名 password: '111111', // 数据库密码 database: 'demo', // 数据库名称 prefix: 'api_', // 默认"api_" } export default new Sequelize( DBConfig.database, DBConfig.username, DBConfig.password, { host: DBConfig.host, port: DBConfig.port, dialect: 'mysql', // 要连接的数据库:mysql、postgres、sqlite 和 mssql 之一 pool: { max: 50, // 池中最大连接数 默认:5 min: 0, // 池中最小连接数 默认:0 idle: 10000, // 连接在被释放之前可以空闲的最长时间(以毫秒为单位)默认:10000 }, timezone: '+08:00', } )
模型 是 Sequelize 的本质. 模型是代表数据库中表的抽象. 在 Sequelize 中,它是一个 Model 的扩展类
// UserModel.js import Sequelize from 'sequelize' import sequelizeMysql from '../utils/sequelizeMysql' // 创建 model const User = sequelizeMysql.define( 'user', { id: { type: Sequelize.UUID, defaultValue: Sequelize.UUIDV1, primaryKey: true, }, username: { type: Sequelize.STRING(255), allowNull: false, // allowNull不设置默认为true }, avatarUrl: { type: Sequelize.STRING(255), field: 'avatar_url', // 自定义表中的列名称 }, createtime: { type: Sequelize.STRING(255), defaultValue: Date.now(), }, isdelete: { type: Sequelize.INTEGER, defaultValue: 0, allowNull: false, }, }, { // true 表名称和 model 相同: diary // false 创建表名称会是复数: diarys freezeTableName: true, // 是否使用默认的 createdAt updatedAt timestamps: false, } ) // 创建表 User.sync({ force: false }) export default User
此只简单列举查询添加操作,更多请点击 API 文档 查看模型查询
// UserDao.js import UserModel from '../model/UserModel' export default class UserDao { // 查询用户 async findById(data) { const { id } = data return await UserModel.findAll({ attributes: { exclude: ['isdelete'] }, // exclude: 返回值排除字段 where: { isdelete: 0, id, }, }) } // 添加用户 async add(data) { return await UserModel.create(data) } }
部分代码出自个人 gitee ...