项目实践中,有后端全用nodejs写后端的,更广泛的是nodej是作为中间层。
使用nodejs处理某些简单业务场景做预处理,nodejs可以做路由处理,更多是nodejs调用其它后端处理请求并返回结果。
比如,nodejs层处理登录session处理,连接redis缓存等,后端网关可以做进一步预处理,比如鉴权
web端 -> nginx负载均衡 -> 多个nodejs程序 -> 后端网关 -> 后端微服务
本文后续主要说明下面一些:
nodejs层需要做路由,分层controller,静态资源处理,对请求做压缩,实现路由和controller结合,nodejs部署,合理规划文件命令和模块规范
koa 本身提供中间件扩展,最重要的是使用中间件
npm i @koa/router koa-bodyparser koa-static koa-combine-routers koa-compress axios jsonfile qs pm2 nodemon
-
@koa/router 路由
-
koa-bodyparser 请求体信息参数拿到,原生是http.on('data')事件获取
-
koa-static 静态文件处理
-
koa-compress 请求体压缩
-
koa-combine-routers 合并多个路由
-
axios 可以用在客户端或服务器端发送http请求
-
jsonfile 读取解析json文件
-
qs 字符串查询
-
nodemon 开发调试自动重启node进程
-
pm2 生产环境node进程管理
curl 测试
curl -X POST -H "Content-Type:application/json;charset=utf-8" -d '{"name":"hello","password":"123"}' http://localhost:3000/api/login
基本
实例化Koa后,use里面操作response返回值
const Koa = require("koa");
const app = new Koa();
app.use(async (ctx) => {
// ctx.body = "hello";
// ctx.resquest 和 ctx.response 不是http中request和response,二是封装后增强的
// ctx.req 和 ctx.res 才是原生http中request和response
ctx.response.type = "text/html";
ctx.response.body = "<html><h1>hello world</h1></html>"; // 完全等价与 ctx.body
});
app.listen(3000, () => {
console.log("listen port 3000");
});
洋葱模型
根据use的注册函数,一层一层执行,next是执行下一个函数
app.use(async (ctx, next) => {
console.log("first start");
await next();
console.log("first end");
});
app.use(async (ctx) => {
console.log("second start");
console.log("second end");
});
输出结果
first start second start second end first end
目录定义
app 主目录编写主文件代码,包括引入路由器,压缩器
common
config 路由配置的基本信息
config/server 将node作为服务器
config/client 将node作为客户端
controller 路由控制
service 处理实际业务逻辑
dao 数据库处理
dist 单页面html等内容
scripts 启动脚本
projectConfig.json 项目配置文件
router 路由和controller映射
接口封装
封装GET\POST\PUT\DELETE
const axios = require("axios");
const projectConfig = require("../util/projectConfigResolver");
const hostBaseUrl = projectConfig.hostBaseUrl;
exports.doHttpGetRequest = function (ctx, requestUrl, params) {
return doHttpRequest(ctx, requestUrl, params, "GET");
};
exports.doHttpPostRequest = function (ctx, requestUrl, params) {
return doHttpRequest(ctx, requestUrl, params, "POST");
};
exports.doHttpPutRequest = function (ctx, requestUrl, params) {
return doHttpRequest(ctx, requestUrl, params, "PUT");
};
exports.doHttpDeleteRequest = function (ctx, requestUrl, params) {
return doHttpRequest(ctx, requestUrl, params, "DELETE");
};
function doHttpRequest(ctx, requestUrl, params, method) {
if ("GET" == method.toUpperCase()) {
return axios({
baseURL: hostBaseUrl,
url: requestUrl,
method: "GET",
params: params,
});
} else if (["PUT", "POST", "DELETE"].includes(method.toUpperCase())) {
return axios({
baseURL: hostBaseUrl,
url: requestUrl,
method: "GET",
data: params,
});
}
}
用户Controller
根据url路由,编写对应的controller,用@koa/router
绑定对应关系
const qs = require("qs");
const baseHttpClient = require("../common/baseHttpClient");
const userRequestUrlMappingResolver = require("../config/client/userRequestUrlMappingResolver");
/**
{
result:{
code:0,
description: 'success'
},
data:{
}
}
*/
class UserController {
async login(ctx) {
const requestUrl = userRequestUrlMappingResolver.login;
console.log(ctx.request.body);
const response = await baseHttpClient.doHttpPostRequest(
ctx,
requestUrl,
JSON.stringify(ctx.request.body)
);
const responseData = qs.parse(response.data);
const responseDataCode = responseData.result.code;
// login successful
if (0 === responseDataCode) {
ctx.body = responseData;
} else {
ctx.body = responseData;
}
}
}
module.exports = new UserController();
路由绑定controller
const Router = require("@koa/router");
const userRouter = new Router();
const userController = require("../controller/userController");
const userServerUrlMappingResolver = require("../config/server/userServerUrlMappingResolver");
userRouter.post(userServerUrlMappingResolver.login, userController.login);
module.exports = userRouter;
定义路由
userServerUrlMapping.json
{
"login": "/api/login"
}
userServerUrlMappingResolver.js
const path = require("path");
const jsonfile = require("jsonfile");
module.exports = jsonfile.readFileSync(
path.join(__dirname, "userServerUrlMapping.json")
);
入口启动koa
const Koa = require("koa");
const path = require("path");
const combineRouters = require("koa-combine-routers");
const bodyParser = require("koa-bodyparser");
const koaStatic = require("koa-static");
const compress = require("koa-compress");
const app = new Koa();
const router = require("./router");
app.use(
compress({
threshold: 2048, // 传输数据量超过2k会压缩响应
})
);
app.use(bodyParser()); // 请求体
app.use(koaStatic(path.join(__dirname, "dist"))); // 静态资源
const unifiedRouters = combineRouters.apply(null, router)(); // 路由合并
app.use(unifiedRouters);
module.exports = app;