mkdir koa-app
npm init -y
git init
yarn add koa -S
yarn add nodemon -D
mkdir app
cd app
touch index.js
vi index.js
cd.. to folder koa-app
vi package.json
add "scripts": "start": "nodemon app/index.js",
# 启动项目
yarn start ==> server on localhost:9000
yarn add commitizen -D
add "scripts": "commit": "git add . && git-cz"
add "config": { "commitizen": { "path": "node_modules/cz-conventional-changelog" }}
前提是:两个账户已分别绑定 git 权限
git remote add github [email protected]:xn213/koa-app.git
git remote add gitee [email protected]:xn213/koa-app.git
yarn commit
# 分别提交
git push github
git push gitee
升级省事: 同时提交到 github 和 gitee
修改 .git/config
[remote "origin"]
url = [email protected]:xn213/koa-app.git
url = [email protected]:xn213/koa-app.git
fetch = +refs/heads/*:refs/remotes/origin/*
这时第一次提交到 gitee, alias: gp == git push 会提示如下图,
由于上面分别提交到 github 和 gitee, 设置了 git remote gitee..., 要使用如下命令提交
git push --set-upstream origin master
此命令会在 .git/config 中添加如下字段
[branch "master"]
remote = origin
merge = refs/heads/master
yarn add koa-router -S
cd app
mkdir router
touch index.js
touch routes.js
yarn add koa-compose koa-bodyparser -S
# 插件 koa-compose 简化引用中间件的写法。
# 插件 koa-bodyparser 处理 post 请求体中的参数
// app/index.js
const compose = require('koa-compose')
const MD = require('./middlewares/')
const app = new Koa()
app.use(compose(MD))
// controllers/test.js
ctx.body = ctx.request.body
这里 post 请求 拿不到 body 的参数,找轮子>插件
// >app
mkdir middlewares
touch index.js
# 注意: koa-bodyparser 处理需放在路由前面
koa-bodyparser
插件只能解析 4 种数据 [ 'json', 'form', 'text', 'xml' ],当上传文件的时候,是获取不到文件的.
这里借助插件 formidable 文档
yarn add formidable -S
// middlewares/formidable.js
const Formidable = require('formidable')
const { tempFilePath } = require('../config')
module.exports = () => {
return async function (ctx, next) {
const form = new Formidable({
multiples: true,
// 上传的临时文件保存路径 抽离到 config/base 一些配置
// uploadDir: `${process.cwd()}/${tempFilePath}`
uploadDir: tempFilePath,
})
await new Promise((reslove, reject) => {
form.parse(ctx.req, (err, fields, files) => {
if (err) {
reject(err)
} else {
// 暂未设置请求头 忽略 请求中添加 二进制 content-type
ctx.request.body = fields
ctx.request.files = files
reslove()
}
})
})
await next()
}
}
// middlewares/index.js 中引入 注册 暴露出去
const formidable = require('./formidable')
const mdFormidable = formidable()
module.exports = [mdFormidable, mdKoaBody, mdRoute, mdRouterAllowed]
另: 抽离配置文件,添加配置文件目录: app/config
// config/index.js
const base = require('./base')
const dev = require('./dev')
const pre = require('./pre')
const pro = require('./pro')
const env = process.env.NODE_ENV || 'dev'
const configMap = {
dev,
pre,
pro,
}
module.exports = Object.assign(base, configMap[env])
编写工具函数 app/utils/index.js
& test.js
cd app
mkdir utils
cd utils
touch index.js
vi index.js i 编辑输入: :wq 保存退出
// utils/index.js
const testUtils = require('./test')
module.exports = {
testUtils,
}
touch test.js
vi test.js i编辑输入
// utils/test.js
const test = 'test utils string'
module.exports = testUtils = () => {
return test
}
挂载到 App context 上下文
// app/index.js
...
const config = require('./config')
const utils = require('./utils')
app.context.utils = config
app.context.utils = utils
...
- 成功
// app/middlewares/response.js
const response = () => {
return async (ctx, next) => {
ctx.res.fail = ({ code, data, msg }) => {
ctx.body = {
code,
data,
msg,
}
}
ctx.res.success = (msg) => {
ctx.body = {
code: 0,
data,
msg: msg || 'success',
}
}
await next()
}
}
module.exports = response
- 失败
// app/middlewares/error.js
const error = () => {
return async (ctx, next) => {
try {
await next()
if (ctx.status === 200) {
ctx.res.success()
}
} catch (err) {
if (err.code) {
ctx.res.fail({ code: err.code, msg: err.message })
} else {
// 程序运行时错误
ctx.app.emit('error', err, ctx)
}
}
}
}
module.exports = error
- 在
middlewares/index.js
中引入
const response = require('./response')
const error = require('./error')
const mdResHandler = response()
const mdErrHandler = error()
module.exports = [mdFormidable, mdKoaBody, mdResHandler, mdErrHandler, mdRoute, mdRouterAllowed]
- 程序运行时错误使用
koa
的错误处理事件,需要在app/index.js
配置
所有的返回值是在 middlewares/error.js
里拦截了一下,如果状态码是 200
,用成功的工具函数包装返回,如果不是则又分为两种情况:一种是我们自己抛出的,包含业务错误码的情况(这种情况我们用失败的工具函数包装返回);另一种是程序运行时报的错,这个往往是我们代码写的有问题(这种情况我们触发 koa
的错误处理事件去处理),针对失败的第二种情况,我们还需要修改启动文件 app/index.js
,添加如下代码:
// 程序本身错误
app.on('error' (err, ctx) => {
if(ctx) {
ctx.body = {
code: 9000,
msg: `程序运行时错误: ${err.message}`
}
}
})
- 测试一下: 在
controllers/test.js
添加如下代码:
2.1. 成功
// controllers/test.js
const getList = async () => {
ctx.body = '返回结果'
}
请求接口, 返回值如下,我们定义的 controller
成功返回
2.2. 业务抛出错误
// controllers/test.js
const getList = async (ctx) => {
const data = ''
// 业务中抛出失败
ctx.utils.assert(data, ctx.utils.throwError(10001, '验证码失效'))
ctx.body = '返回结果'
}
2.3. 程序本身报错
这时添加一行 a = b
, 这里 b
未定义,则为程序本身错误, 触发 koa error
事件
const getList = async (ctx) => {
const a = b
ctx.body = '返回结果'
}
直接用插件 @koa/cors npm/@koa/cors
app/middlewares/index.js
中引入,实例化,导出
// middlewares/index.js 跨域处理
const cors = require('@koa/cors')
const mdCors = cors({
origin: '*',
credentials: true,
allowMethods: ['GET', 'HEAD', 'PUT', 'POST', 'DELETE', 'PATCH'],
})
module.exports = [
mdFormidable,
mdKoaBody,
mdCors,
mdResHandler,
mdErrorHandler,
mdRoute,
mdRouterAllowed,
]
插件:log4js
// middlewares/log.js
const log4js = require('log4js')
const { flag, level, outDir } = require('../config').logConfig
log4js.configure({
appenders: {
cheese: {
type: 'file',
filename: `${outDir}/receive.log`,
},
},
categories: {
default: {
appenders: ['cheese'],
level: 'info',
},
},
pm2: true,
})
const logger = log4js.getLogger()
logger.level = level
module.exports = () => {
return async (ctx, next) => {
const { method, path, origin, query, body, headers, ip } = ctx.request
const data = {
method,
path,
origin,
query,
body,
ip,
headers,
}
await next()
if (flag) {
const { status, params } = ctx
data.status = status
data.params = params
data.result = ctx.body || 'no content'
if (ctx.body.code !== 0) {
logger.error(JSON.stringify(data))
} else {
logger.info(JSON.stringify(data))
}
}
}
}
app/middlewares/index.js
引入/注册/配置
const log = require('./log')
const mdLogger = log()
module.exports = [
mdFormidable,
mdKoaBody,
mdCors,
mdLogger,
mdResHandler,
mdErrHandler,
mdRoute,
mdRouterAllowed
]