原文地址:koa+mysql+vue+socket.io全栈开发之web api篇javascript
目标是创建一个 web QQ的项目,使用的技术栈以下:前端
本篇则讲叙服务端的搭建,之因此使用 koa,而不使用其余封装过的框架,好比 Egg.js, Thinkjs。由于在我看来,koa2 已经够方便,插件也足够多,彻底能够根据本身的需求,像搭积木同样构建出最适合业务需求的框架。这样不但摒弃了不少用不到的插件,使整个框架更加精简,也能对整个框架知根知底,减小了不少不可预知因素的影响。vue
固然我以为最主要的是我比较懒😄,不想再去学其余框架特有的api,特有的配置。由于前端有太多框架太多api须要掌握了,对于非互联网公认的技术标准,我以为学习的优先级仍是要靠后一点的。由于这些个框架,三天两头就冒出个热门的,简直多不胜数,学不过来啊,而koa基本都是这些框架的底层,明显靠谱多了。java
这几个koa插件大部分项目八九不离十要用到:mysql
public #公共目录
src #前端目录
server #后端目录
├── common #工具
├── config #配置文件
├── controller #控制器
├── daos #数据库访问层
├── logs #日志目录
├── middleware #中间件目录
├── socket #socketio目录
├── app.js #入口文件
└── router.js #路由
复制代码
主要就是几个中间件配置须要注意一下,这里同时还加载了 socket.io 服务。socket.io 相关的基本知识点能够看我以前写的文章关于socket.io的使用。git
//app.js
//...
const path = require("path");
const baseDir = path.normalize(__dirname + "/..");
// gzip
app.use(
compress({
filter: function(content_type) {
return /text|javascript/i.test(content_type);
},
threshold: 2048,
flush: require("zlib").Z_SYNC_FLUSH
})
);
// 解析请求
app.use(
koaBody({
jsonLimit: 1024 * 1024 * 5,
formLimit: 1024 * 1024 * 5,
textLimit: 1024 * 1024 * 5,
multipart: true, // 解析FormData数据
formidable: { uploadDir: path.join(baseDir, "public/upload") }//上传文件目录
})
);
// 设置静态目录
app.use(static(path.join(baseDir, "public"), { index: false }));
app.use(favicon(path.join(baseDir, "public/favicon.ico")));
//cors
app.use(
cors({
origin: "http://localhost:" + config.clientPort,
credentials: true,
allowMethods: ["GET", "POST", "DELETE"],
exposeHeaders: ["Authorization"],
allowHeaders: ["Content-Type", "Authorization", "Accept"]
})
);
//json-web-token中间件
app.use(
jwt({
secret: config.secret,
exp: config.exp
})
);
// 登陆验证中间件,exclude 表示不验证的页面,include 表示要验证的页面
app.use(
verify({
exclude: ["/login", "/register", "/search"]
})
);
// 错误处理中间件
app.use(errorHandler());
// 路由
addRouters(router);
app.use(router.routes()).use(router.allowedMethods());
// 处理404
app.use(async (ctx, next) => {
log.error(`404 ${ctx.message} : ${ctx.href}`);
ctx.status = 404;
ctx.body = { code: 404, message: "404! not found !" };
});
// 处理中间件和系统错误
app.on("error", (err, ctx) => {
log.error(err); //log all errors
ctx.status = 500;
ctx.statusText = "Internal Server Error";
if (ctx.app.env === "development") {
//throw the error to frontEnd when in the develop mode
ctx.res.end(err.stack); //finish the response
} else {
ctx.body = { code: -1, message: "Server Error" };
}
});
if (!module.parent) {
const { port, socketPort } = config;
/** * koa app */
app.listen(port);
log.info(`=== app server running on port ${port}===`);
console.log("app server running at: http://localhost:%d", port);
/** * socket.io */
addSocket(io);
server.listen(socketPort);
}
复制代码
这里解释一下 koa-cors 参数的设置,我项目使用的是 json web token,须要把认证字段Authorization添加到header,前端获取该header字段,以后给后台发送http请求的时候,再带上该Authorization。github
至于 json web token的原理,网上资料齐全,这里再也不介绍了。web
app.use(
cors({
origin: "http://localhost:" + config.clientPort, // 访问header,要写明具体域名才行
credentials: true, //将凭证暴露出来, 前端才能获取cookie
allowMethods: ["GET", "POST", "DELETE"],
exposeHeaders: ["Authorization"], // 将header字段expose出去
allowHeaders: ["Content-Type", "Authorization", "Accept"] // 容许添加到header的字段
})
);
复制代码
koa 的中间件就是 web开发的利器,经过它能够很是方便的实现 强类型语言中的 aop 切面编程,而koa2 中间件 的编写也足够简单 koajs。sql
项目在如下几个地方都用中间件进行了封装,不少重复的样板代码所以得以简化。vuex
就以最简单的错误处理中间件为例子,若是不使用错误处理中间件,咱们须要每一个控制器方法进行 try{…} catch{…} ,其余中间件编写方式相似,就再也不介绍。
/** * error handler 中间件 */
module.exports = () => {
return async (ctx, next) => {
try {
await next();//没有错误则进入下一个中间件
} catch (err) {
log.error(err);
let obj = {
code: -1,
message: '服务器错误'
};
if (ctx.app.env === 'development') {
obj.err = err;
}
ctx.body = obj
}
};
};
// 控制器代码使用error handler中间件后,每一个方法都不须要 try catch处理错误,记录错误日志,处理逻辑都集中在中间件里面了。
exports.getInfo = async function(ctx) {
// try {
const token = await ctx.verify();
const [users, friends] = await Promise.all([
userDao.getUser({ id: token.uid }),
getFriends([token.uid])
]);
const msgs = applys.map(formatTime);
ctx.body = {
code: 0,
message: "好友列表",
data: {
user: users[0],
friends: mergeReads(friends, reads),
groups,
msgs
}
};
// } catch (err) {
// log.error(err);
// let obj = {
// code: -1,
// message: "服务器错误"
// };
// if (ctx.app.env === "development") {
// obj.err = err;
// }
// ctx.body = obj;
// }
};
复制代码
路由配置只使用了get,post 方法,固然要使用 put,delete也只是改一下名字就行。
// router.js
const { uploadFile } = require('./controller/file')
const { login, register } = require('./controller/sign')
const { addGroup, delGroup, updateGroup } = require('./controller/group')
//...
module.exports = function (router) {
router
.post('/login', login)
.post('/register', register)
.post('/upload', uploadFile)
.post('/addgroup', addGroup)
.post('/delgroup', delGroup)
.post('/updategroup', updateGroup)
//...
};
复制代码
以updateInfo方法为例,koa2 已经全面支持 async await,编写方式和同步代码没多大区别。
exports.updateInfo = async function (ctx) {
const form = ctx.request.body;
const token = await ctx.verify();
const ret = await userDao.update([form, token.uid]);
if (!ret.affectedRows) {
return ctx.body = {
code: 2,
message: '更新失败'
};
}
ctx.body = {
code: 0,
message: '更新成功'
};
}
复制代码
接着下一编就是基于 mysql 构建 数据库访问层。