主题
koa2
准备
使用
koa2-generator
生成koa2
模板shellnpx koa2-generator -e [projectName]
进入目录,并且安装依赖包
shellcd [projectName] npm install
引入
cross-env
检测开发或线上环境,已做不同的处理shellnpm install cross-env --save-dev
引入
nodemon
自动重启项目shellnpm install nodemon --save-dev
重构 package.json
json"scripts": { "start": "node ./bin/www", "dev": "cross-env NODE_ENV=dev nodemon ./bin/www", "prd": "cross-env NODE_ENV=production nodemon ./bin/www" }
遇到找不到xx模块问题
把 node_modules 删了,重新下载依赖包
npm install
解析和重构 app.js
js
const Koa = require('koa')
const app = new Koa() // new 一个 koa 实例,赋予 app
const views = require('koa-views') // 用于视图层
// const co = require('co') // 将koa1包中使用的Generator函数转换成Koa2中的async函数
// const convert = require('koa-convert') // 将koa1包中使用的Generator函数转换成Koa2中的async函数
const json = require('koa-json') // 用于解析json
const onerror = require('koa-onerror') // 用于处理错误
const bodyparser = require('koa-bodyparser') // 解析 post data
const logger = require('koa-logger') // 用于生成配置文件
const debug = require('debug')('koa2:server')
const path = require('path')
const fs = require('fs')
const morgan = require('koa-morgan') // 引入 koa-morgan 模块,用于写日志
const config = require('./config') // 导入配置文件,在里面可以修改端口
// 引入路由
const index = require('./routes/index')
const users = require('./routes/users')
// 错误处理
onerror(app)
// middlewares 中间件
app.use(bodyparser())
.use(json())
.use(logger())
.use(require('koa-static')(__dirname + '/public'))
.use(views(path.join(__dirname, '/views'), {
options: {settings: {views: path.join(__dirname, 'views')}},
map: {'ejs': 'ejs'},
extension: 'ejs'
}))
// logger 日志
app.use(async (ctx, next) => {
// 计算运行完全部程序的时间
const start = new Date()
await next()
const ms = new Date() - start
console.log(`${ctx.method} ${ctx.url} - $ms`)
})
const ENV = process.env.NODE_ENV;
if(ENV == 'dev'){
// 测试和开发环境
app.use(morgan('dev'));
}else{
// 线上环境,向日志中追加内容
const logname = path.join(__dirname, 'logs', 'access.log')
const writeStream = fs.createWriteStream(logname, {
flags: 'a' // 追加的方式
})
app.use(morgan('combined', {
stream: writeStream
}));
}
// 注册路由
app.use(index.routes(), index.allowedMethods())
app.use(users.routes(), users.allowedMethods())
app.on('error', function(err, ctx) {
console.log(err)
logger.error('server error', err, ctx)
})
module.exports = app.listen(config.port, () => {
console.log(`Listening on http://localhost:${config.port}`)
})
重构日志
引入包
shellnpm install koa-morgan --save
引入 fs 模块
引入以上
42~55
行的代码再主文件下创建
/logs/access.log
路由使用
重构模板中的路由
如以上
app.js
一样删除在app.js
中的router
在
app.js
引入/routers
下的路由,如16-18
行。并且注册,如57-58
行同时改造
/routers
下的路由文件jsconst router = require('koa-router')() router.get('/', async function (ctx, next) { ctx.state = { title: 'koa2 title' } await ctx.render('index', {title: ctx.state}) }) module.exports = router
获取客户端传来的数据
get
jsconst { username } = ctx.query
post
jsconst { username } = ctx.request.body
返回数据给客户端
- js
ctx.body = 'hello world'
很多时候,返回的应该是一个模型
创建
/model/resModel.js
来创建基本模型jsclass BaseModel { constructor(data, message) { if (typeof data === 'string') { // 如果 data 是字符串(不是对象),则表明只返回一个信息 this.message = data data = null // 两个设置为 null ,则不走下面的判断了 message = null } if (data) { // this.data = data } if (message) { this.message = message } } } // 成功的模型,code = 1 class SuccessModel extends BaseModel { constructor(data, message) { super(data, message) this.code = 1 } } // 失败的模型,code = 0 class ErrorModel extends BaseModel { constructor(data, message) { super(data, message) this.code = 0 } } module.exports = { SuccessModel, ErrorModel }
在路由中使用
jsconst { SuccessModel, ErrorModel } = require('../model/resModel') router.get('/', (ctx, next) => { const { username } = ctx.query ctx.body = new SuccessModel({ username }) });
使用session
引入包
shellnpm install koa-generic-session --save
再
app.js
中引入并注册jsconst session = require('koa-generic-session'); // 在日志下注册 app.keys = ['hfkjahkj'] app.use(session({ // 配置 cookie cookie: { path: '/', httpOnly: true, maxAge: 24 * 60 * 60 * 1000 } }))
在路由中设置 / 获取
jsctx.session.username = 'kingmusi'
操作 mysql
引入包
shellnpm install mysql --save
创建
/db
文件夹管理创建
/db/config.js
文件管理 mysql 的基本参数js// 获取环境变量 const env = process.env.NODE_ENV let MYSQL_CONF if (env === 'dev') { MYSQL_CONF = { host: 'localhost', user: 'root', password: '123', port: '3306', database: 'myblog' } } if (env === 'production') { MYSQL_CONF = { host: 'localhost', user: 'root', password: '123', port: '3306', database: 'myblog' } } module.exports = MYSQL_CONF
创建
/db/helper.js
文件管理 mysql 的自定义方法jsconst MYSQL_CONF = require('./config.js') const mysql = require('mysql') // 创建连接对象 const con = mysql.createConnection( MYSQL_CONF ) // 开始连接 con.connect() // 统一执行 sql 的函数 function exec(sql) { return new Promise((resolve, reject) => { con.query(sql, (err, result) => { if (err) reject(err) else resolve(result) }) }) } module.exports = { exec, // 执行 sql 的函数 escape: mysql.escape // sql 预处理,防止 sql 注入 }
路由的使用示例
jsrouter.get('/', async (ctx, next) => { const { username } = ctx.query const sql = `select password from users where username=${escape(username)}` const result = await exec(sql) ctx.body = result });
用 escape 就不用
‘’
如果不用,则sql应该这样写
jsconst sql = `select password from users where username='${username}''`
mysql 的逻辑应当和 router 的逻辑分开,这样能更好的维护
创建
/controller/[路由名]s.js
,用于管理 mysql 的逻辑jsconst { exec, escape } = require('../db/helper') const getPassword = async ( username ) => { const sql = `select password from users where username=${escape(username)}` const result = await exec(sql) return result[0].password } module.exports = { getPassword }
路由调用
jsconst { SuccessModel, ErrorModel } = require('../model/resModel') const { getPassword } = require('../controller/indexs') /* GET home page. */ router.get('/', async (ctx, next) => { const { username } = req.query const password = await getPassword(username) ctx.body = new SuccessModel({ password }) });