Koa2脚手架框架搭建流程
记录一下自己之前使用koa2创建项目时初始化的整个流程:
- 路由
- 数据库
- 返回结构定义
- 封装DBError
- Token
路由
- 定义在app.js中
const api = require('./routes/api/index');
router.use('/api', api.routes(), api.allowedMethods());
app.use(router.routes(), router.allowedMethods());
router.post('/updateUser', token_valide,user_controller.updateUser);//这里token_valide是路由中间件,按顺序执行
- index.js
var router = require('koa-router')();
var user_router = require('./user_router');
router.use('/users', user_router.routes(), user_router.allowedMethods());
module.exports = router;
数据库
- 使用sequelize操作mysql,用以下命令自动生成操作需要的模块
sequelize-auto -o "./schema" -d appstore -h 111.111.11.102 -u root -p 3306 -x root -e mysql
- db.js
const Sequelize = require('sequelize')
const config = {
database: '',
username: '',
password: '',
host: '',
port: 3306
}
const DATABASE = new Sequelize(config.database, config.username, config.password, {
host: config.host,
dialect: 'mysql', // 数据库方言
pool: {
max: 5,
min: 0,
idle: 30000
},
define: {
timestamps: false
}
})
module.exports = {
DATABASE
}
- 数据库操作类封装
/**
* Created by lipeng on 17/8/6.
*/
// models/user.js
const db = require('../config/db')
const DATABASE = db.DATABASE // 引入数据库
const User = DATABASE.import("../schema/user.js"); // 用sequelize的import方法引入表结构,实例化了User。
const ResponseCodes = require('../util/ResponseCodes')
const DBError = require('./dberror');
class UserTable {
// 数据库操作
static queryAll() {
return User.findAll();
}
/**
* 查询用户信息
* @param name 姓名
* @returns {Promise.<*>}
*/
static async findUserByName (username) {
try {
const userInfo = await User.findOne({
where: {
username
}
})
return userInfo ;
}catch(err) {
throw new DBError(ResponseCodes.DBERROR,err);
}
}
/**
* 创建用户
* @param user
* @returns {Promise.}
*/
static async createUser (user) {
try {
let rows = await User.create(user)
return {"rows":rows};
}catch(err) {
throw new DBError(ResponseCodes.DBERROR,err);
}
}
}
module.exports = UserTable;
返回结构定义
- 数据库操作错误封装,抛出异常
static async findUserByName (username) {
try {
const userInfo = await User.findOne({
where: {
username
}
})
return userInfo ;
}catch(err) {
throw new DBError(ResponseCodes.DBERROR,err);
}
}
- 封装的DBError
class DBError extends Error{
//构造方法
constructor(code,error){
super();
this.error = error;
this.code = code;
}
}
module.exports = DBError;
- 中间件统一处理数据库异常
/**
* Created by lipeng on 17/8/7.
*/
const DBError = require('../models/dberror');
/**
* 在app.use(router)之前调用
*/
var dberror_response = async (ctx, next) => {
//先去执行路由
try {
//先去执行路由
await next();
} catch (error) {
if(error instanceof DBError){
ctx.status = 200;
ctx.error(error.code,error.error);
}
//继续抛,让外层中间件处理日志
throw error;
}
}
module.exports = dberror_response;
- 网络请求返回封装
- 中间件封装一层 response
app.use(response);
var ResponseCodes = require('../util/ResponseCodes');
module.exports = async (ctx, next) => {
ctx.error = ( code, msg,data) => {
if(!msg){
msg = ResponseCodes.getErrorInfo(code);
}
ctx.body = { code: code, msg:msg, data:data};
}
ctx.success = (data) => {
ctx.body = { errno: 0, msg:"success", data:data };
}
await next()
}
- 定义ResponseCodes,这里只定义错误code,正确的默认是0
/**
* Created by lipeng on 17/8/6.
*/
/**
* API错误名称
*/
var ResponseCodes = {};
ResponseCodes.UNKNOW_ERROR = "-1";
ResponseCodes.USER_NOT_EXIST = "-2";
ResponseCodes.USER_EXIST = "-3";
ResponseCodes.DBERROR = "-4";
/**
* API错误名称对应的错误信息
*/
const error_map = new Map();
error_map.set(ResponseCodes.UNKNOW_ERROR, '未知错误');
error_map.set(ResponseCodes.USER_NOT_EXIST, '用户不存在');
error_map.set(ResponseCodes.USER_EXIST, '用户名已存在');
error_map.set(ResponseCodes.DBERROR, '数据库连接错误');
//根据错误名称获取错误信息
ResponseCodes.getErrorInfo = (error_name) => {
var error_info;
if (error_name) {
error_info = error_map.get(error_name);
}
//如果没有对应的错误信息,默认'未知错误'
if (!error_info) {
error_name = UNKNOW_ERROR;
error_info = error_map.get(error_name);
}
return error_info;
}
module.exports = ResponseCodes;
Token
- 引入两个库
const bcrypt = require('bcryptjs')
const jwt = require('jsonwebtoken')
- 生成token
/**
* 登录
* @param ctx
* @param next
* @returns {Promise.}
*/
static async loginUser (ctx,next) {
let body = ctx.request.body;
//判断是否用户名已经存在
let user = await UserTable.findUserByName(body.username);
if(user){
// 判断前端传递的用户密码是否与数据库密码一致
if (bcrypt.compareSync(body.password, user.password)) {
// 用户token
const userToken = {
username: user.username,
id: user.id
}
const token = jwt.sign(userToken, secret.sign, {expiresIn: '1h'}) // 签发token
user.token = token;
ctx.success(user)
}else{
ctx.error(ResponseCodes.LOGIN_ERROR);
}
}else{
ctx.error(ResponseCodes.LOGIN_ERROR);
}
}
- 请使用expiresIn:以秒为单位或描述的时间跨度字符串表示rauchg / MS。如:60,”2 days”,”10h”,”7d”
{expiresIn: 60} // 有效期60秒(没有时间单位以秒为准)
{expiresIn: "2 days"} // 有效期 2天 (后缀为时间单位)下面的类似
......
('1d') // 86400000
('10h') // 36000000
('2.5 hrs') // 9000000
('2h') // 7200000
('1m') // 60000
('5s') // 5000
('1y') // 31557600000
- verify时返回的err的值
"err": {
"name": "TokenExpiredError",
"message": "jwt expired", // token过了有效期
"expiredAt": "2016-11-07T03:31:25.000Z"
}
"err": {
"name": "JsonWebTokenError",
"message": "invalid token" // 伪造/无效的token
}
- Token处理中间件
/**
* Created by lipeng on 17/8/7.
*/
var jwt = require('jsonwebtoken');//用来创建和确认用户信息摘要
const secret = require('../config/secret.json')
const ResponseCodes = require('../util/ResponseCodes')
const DBError = require('../models/dberror');
function verify(token) {
return new Promise(( resolve, reject ) => {
jwt.verify(token, secret.sign, async function (err, decode) {
if (err) { // 时间失效的时候/ 伪造的token
resolve(new DBError(ResponseCodes.TOKEN_ERROR, err)) ;
} else {
console.log("decode.msg:" + JSON.stringify(decode)); // today is a good day
resolve(decode)
}
})
})
}
// 检查用户会话
module.exports = async (ctx, next) => {
console.log('检查post的信息或者url查询参数或者头信息');
let body = ctx.request.body;
var token = body.token || ctx.query.token || ctx.request.headers['x-access-token']; // 从body或query或者header中获取token
// 解析 token
if (token) {
let result = await verify(token);
if(result instanceof DBError){
throw result;
}else{
body.userToken = result;
await next();
}
}else{
throw new DBError(ResponseCodes.TOKEN_ERROR);
}
}