我在实际项目开发过程中,经常要自测,这个时候并不能完全指望服务端 mock 接口和数据,所以其实一段时间了,前前后后沉淀了有这么个 ts-koa 的应用(koa-lite),多用于用于调试接口和 mock 数据,当然,做个改装,也可以用于正式的服务端项目
当然有人要问,为啥不用 nest、egg 这些脚手架,主要原因很简单吧,这些脚手架有额外的上手成本,你要熟悉他们的代码架构,然后相对应的代码文件也比较多,并且功能很齐全。但是咱不需要那么齐全,毕竟初衷是为了用于个人自测项目和 Node.js 学习,一开始就用那么齐全的库,效果反而适得其反
回到项目本身吧,做了一些基础且顺应技术趋势的事情
支持 ts
用 ts-node 编译 ts 代码,nodemon 可以监听文件和热更(根目录下可以通过 nodemon.json 配置规则,参考 nodemon 的配置文件)
入口文件
提供了 http 和 websocket 两种应用(app.ts 和 wsApp.ts),分别占用不同的端口,可以配置不同的 script 命令,比如:
"dev": "nodemon -e ts app",
"dev:ws": "nodemon -e ts wsApp",
可以开两个终端同时启动俩应用
websocket 应用是借助了koa-websocket
这个中间件实现的,wsApp.ts
的基础代码如下:
import Koa from "koa";
import consola from "consola";
import websockify from "koa-websocket";
const wsOptions = {};
const app = websockify(new Koa(), wsOptions);
app.ws.use((ctx, next) => {
return next(ctx);
});
app.ws.use((ctx) => {
ctx.websocket.send("===Hello");
ctx.websocket.on("message", function (msg) {
console.log("===recive msg", msg);
});
ctx.websocket.on("close", () => {
console.log("===close websocket");
});
});
/**
* Create HTTP server.
*/
const host = process.env.HOST || "localhost";
const port = Number(process.env.PORT) || 3001;
const server = app.listen(port, host, () => {
consola.ready({
message: `websocket server listening on ws://${host}:${port}`,
badge: true,
});
});
http
rest api
借助 koa-router
,具体 get、post 接口的实现可以参考项目里的示例
建议不同模块按照前缀区分,然后分为不同文件,在入口文件中引入
//...
app.use(homeRouter.routes()).use(homeRouter.allowedMethods());
app.use(userRouter.routes()).use(router.allowedMethods());
文件接口
借助 koa-body
实现上传。这个中间件不仅用于处理 post 请求的数据,还能处理文件类型,即 multipart/form-data
。如果是 koa-bodyparser
,还要结合 koa-multer
处理文件数据,而且不能和 koa-body
共用,会有冲突
// app.ts
import koaBody from "koa-body";
//...
app.use(
koaBody({
multipart: true,
formidable: {
// the max size of uploading file
maxFileSize: 200 * 1024 * 1024,
},
})
);
// homeRouter.ts
function saveFile(file) {
// 文件读写流
const reader = fs.createReadStream(file.path);
let filePath = path.join(__dirname, "/static/upload/") + `/${file.name}`;
const upStream = fs.createWriteStream(filePath);
reader.pipe(upStream);
}
homeRouter.post("/upload", async (ctx, next) => {
// console.log(ctx.request.files);
const files = ctx.request.files.file;
// 支持多文件
if (files.length > 1) {
for (let file of files) {
saveFile(file);
}
} else if (files) {
saveFile(files);
}
ctx.body = "Upload successfully.";
});
借助 koa-send
实现下载
import send from "koa-send";
// homeRouter.ts
homeRouter.get("/download/:filename", async (ctx, next) => {
let filename = ctx.params.filename;
let filePath = `./static/download/${filename}`;
// mime type, active the download window in browser
ctx.attachment(filePath);
await send(ctx, filePath);
console.log("download successfully.");
});
之前写过一篇 mock 文件接口的文章,参考 mock 文件接口
跨域处理
借助 koa2-cors
定义允许跨域的规则
import cors from "koa2-cors";
//...
app.use(
cors({
origin: function (ctx) {
// 设置允许来自指定域名请求
if (ctx.url === "/test") {
return "*";
}
return "http://localhost:8080";
},
maxAge: 5,
credentials: true,
allowMethods: ["GET", "POST", "PUT", "DELETE", "OPTIONS"],
allowHeaders: ["Content-Type", "Authorization", "Accept"],
exposeHeaders: ["WWW-Authenticate", "Server-Authorization"],
})
);
//...
模板引擎
基于 koa-views
和 ejs
。当然也可以用其他模板引擎,看个人喜好了,我主要是觉得 ejs 更贴近于 html 的形式(对比 pug,缩进那一套确实不太能 get)
import views from "koa-views";
//...
app.use(
views(path.join(__dirname, "./views"), {
extension: "ejs",
})
);
静态资源托管
基于 koa-static
,指定一个单独的文件夹来存放静态资源文件
import koaStatic from "koa-static";
//...
// 静态资源目录(相对入口文件app.ts)
const staticPath = "./static";
app.use(koaStatic(path.join(__dirname, staticPath)));
其他
项目相对比较简单,欢迎 comment 和 fork(别忘了 star 哈)。后面主要是个人使用过程中持续迭代,有好的建议欢迎提呀~