koa 脚手架
本人弄了一个koa脚手架,koa和mongodb组合而成, 已上传GitHub. 👏 👏 👏 👏
目录结构如上,一个控制器(也就是写代码的地方),一个日志,一个数据库模型,一个路由,一个工具,和入口文件。
写代码只要添加路由 填充控制器就好了,如图所示
下面解析下有哪些构成的👇
koa
const Koa = require('koa');
const app = new Koa();
// response
app.use(ctx => {
ctx.body = 'Hello Koa';
});
app.listen(3000);
复制代码
router
官方例子
var Koa = require('koa');
var Router = require('koa-router');
var app = new Koa();
var router = new Router();
router.get('/', (ctx, next) => {
// ctx.router available
});
app
.use(router.routes())
.use(router.allowedMethods());
复制代码
实战例子
// routers/index.js
const Router = require('koa-router')
const Controllers = require('./controller')({})
const dateNumberController = Controllers('date_number')
const router = new Router({
prefix: '/api'
})
router
.post(dateNumberController.getPath('add'), dateNumberController.add)
.get(dateNumberController.getPath(`select`), dateNumberController.find)
.post(dateNumberController.getPath(`remove`), dateNumberController.delect)
.post(dateNumberController.getPath(`update`), dateNumberController.update)
module.exports = router
// routers/controller.js
const controllerFunction = ({basePath = '../controllers/', router_path_function}) => (controllerName) => {
const divController = require([basePath, controllerName].join(''))
const prefix = controllerName
class Controller extends divController {
prefix = ''
getPath(name){
return router_path_function ? router_path_function(this.prefix, name) : `/${this.prefix}/${name}`
}
}
const controller = new Controller()
controller.prefix = prefix
return controller
}
module.exports = controllerFunction
// controllers/date_number.js
见下面的 mongodb 里面的模型代码
// main.js
const router = require('./routers')
app.use(router.routes());
复制代码
bodyparser
有了路由,但是我们还需要前端传递过来的参数,所以需要先获取参数
例子
app.use(async ctx => {
ctx.body = ctx.request.body;
});
复制代码
这时候post请求就可以解析了,不管是application/json还是application/x-www-form-urlencoded
效果
koa-parameter
拿到参数后我们需要校验它
// main.js
const parameter = require("koa-parameter")
const error = require("koa-json-error")
app.use(parameter(app));
// 这个是json错误处理,可以自动抛出http status 422
app.use(
error({ postFormat: (e, { stack, ...rest }) => ({ stack, rest }) })
)
// ctx里面,比如路由里面
ctx.verifyParams({
date: { type: "string", required: true },
list: { type: "array", required: true },
})
复制代码
mongodb
拿到参数,我们下一步就是需要操作数据了
基础
初始化
安装mongodb,然后npm安装mongoose, 如下初始化即可
const mongoose = require('mongoose')
// db是数据库名称哦,没有的话会自动创建
const DB_ADDRESS = "mongodb://localhost:27017/db"
mongoose.connect(DB_ADDRESS, {useNewUrlParser: true, useUnifiedTopology: true}, err => {
if (err) {
log.fatal({msg: '[Mongoose] database connect failed!', err})
} else {
console.log('[Mongoose] database connect success!')
}
})
module.exports = mongoose
复制代码
建立模型
使用的时候,我们需要先建立集合(在mysql里面叫表),但是MD更灵活,可以直接在代码里面操作,我们先建立模型。
const mongoose = require('mongoose');
const { Schema, model } = mongoose;
// 数据模型
let DateNumberSchema = new Schema({
date: { type: String, required: true, unique: true },
list: { type: Array, require: true },
});
module.exports = model('DateNumber', DateNumberSchema);
复制代码
操作数据库
查
const data = await DateNumber.find()
复制代码
新增
// 要和模型对应
const data = await to( new DateNumber({date, list}).save()
复制代码
删
const data = await DateNumber.deleteOne({date: date})
复制代码
改
const data = await DateNumber.updateOne({date}, {$set: {list}})
复制代码
例子
const DateNumber = require('../models/dateNumber')
class DateNumberController {
prefix = ''
getPath(name){
return `/${this.prefix}/${name}`
}
async add(ctx, next){
ctx.verifyParams({
date: { type: "string", required: true },
list: { type: "array", required: true },
})
const {date, list} = ctx.request.body
const [err, data] = await to( new DateNumber({date, list}).save() )
if(err) return ctx.throw(500, err)
ctx.response.body = data
}
async find(ctx, next){
const data = await DateNumber.find()
ctx.response.body = data.join('\n')
log.info('find')
}
async delect (ctx, next){
ctx.verifyParams({
date: { type: "string", required: true },
})
const {date} = ctx.request.body
const data = await DateNumber.deleteOne({date: date})
ctx.response.body = data
}
async update(ctx, next){
ctx.verifyParams({
date: { type: "string", required: true },
list: { type: "array", required: true },
})
const {date, list} = ctx.request.body
const [err, data] = await to( DateNumber.updateOne({date}, {$set: {list}}) )
if(err) return ctx.throw(500, err)
ctx.response.body = data
}
}
module.exports = new DateNumberController()
复制代码
对应关系(来自菜鸟教程)
SQL术语/概念
MongoDB术语/概念
解释/说明
database
database
数据库
table
collection
数据库表/集合
row
document
数据记录行/文档
column
field
数据字段/域
index
index
索引
table
joins
表连接,MongoDB不支持
primary
key
primary key主键,MongoDB自动将_id字段设置为主键
MAC的需要先运行mongod,然后再开一个窗口,不然会提醒找不到服务;
Windows有自带的可视化软件,安装的时候install mongoDB compass勾上即可
to function
上面有一个代码用到了to
to是对promise的一个封装,变成[err, data] 这种形式,这样有一个好处是不需要写try catch,又可以平级处理错误。
to是全局的,在main.js引入
进阶
分页查询
new DateNumber.find().skip(条数*(页码-1)).limit(条数)
复制代码没有设置索引的话会很慢
连左集合查询
db.orders.aggregate([
{
$lookup:
{
from: "inventory",
localField: "item",
foreignField: "sku",
as: "inventory_docs"
}
}
])
复制代码
日志
使用log4js实现日志
使用方式
var log4js = require('log4js');
var logger = log4js.getLogger();
logger.level = 'debug';
logger.debug("Some debug messages");
复制代码
Level
等级作用是,输出>=当前级别的日志
输出设置
我设置的是,info正常输出,输出的时候按日期分(这样文件不会太大),如果遇到error这种大问题就发邮件,例子如下,包含测试代码,你配置下email里面的账号密码就可以运行了
const config = {
// 例子
email: {
host: 'smtp.qq.com',
auth: {
user: '你的qq号@qq.com',
pass: '你的密码,',
},
recipients: '发送方@126.com'
}
}
const LOGINFO = {
appenders: {
info: {
type: "DateFile",
category: 'dateFileLog',
filename: path.join(__dirname, './log/info/'),
pattern: "yyyy-MM-dd.log",
alwaysIncludePattern: true
},
email: {
type: '@log4js-node/smtp',
//发送邮件的邮箱
sender: config.email.auth.user,
//标题
subject: 'Latest error report',
SMTP: {
host: config.email.host,
auth: config.email.auth,
},
recipients: config.email.recipients
}
},
}
const log4js = require('log4js')
log4js.configure(LOGINFO)
const log_info = log4js.getLogger()
const log_error = log4js.getLogger('error')
global.log = {
debug: log_info.debug.bind(log_info),
info: log_info.info.bind(log_info),
warn: log_info.warn.bind(log_info),
error: log_error.error.bind(log_error),
fatal: log_error.fatal.bind(log_error),
}
// 这个是测试代码
setTimeout(() => {
log.fatal({
msg: '测试',
err: 'fatal'
})
}, 400)
复制代码邮箱密钥获取方式: 进入qq邮箱,设置----> 账户 ------->开启服务:POP3/SMTP服务 ----->生成授权码log.fatal({
msg: '测试',
err: 'fatal'
})
复制代码
效果
其他
各位大哥哥小姐姐麻烦点个赞,我要冲3级,谢谢
-- 完 --