使用前后端完全分离的方式构建项目时,在前端需要预留AJAX请求的接口,实现页面数据的展示等操作。此时,前端开发人员等待后台人员为我们写测试接口是不太现实的。所以,我们可以自己搭建一个简单的后台服务,仅仅返回前端需要的数据。
使用Node.js搭建一个后台服务,后台框架选择koa,一个轻量级的框架,相较于常见的Express更便捷。实现源码
// load opensource code
const Koa = require('koa');
const staticServer = require('koa-static');
const KoaRouter = require('koa-router');
const bodyParser = require('koa-bodyparser');
对于一个后台服务通常需要考虑路由(静态资源路由、非静态资源路由)、参数解析以及日志。
这里使用koa-static模块,直接使用npm install安装即可,使用也很方便。
const app = new Koa();
app.use(staticServer(__dirname + ‘/static’));
这里使用了“use”接口,这个可以加载中间件,拦截HTTP请求,与Express的“use”类似。它允许自己定义一个中间件,比如,自己写一个拦截用户访问的remote ip的中间件。
/**
* 访问日志
*
*/
app.use(async (ctx, next) => {
let remoteIP = ctx.request.ip;
let origin = ctx.request.origin;
let url = ctx.request.url;
logger.info(`Remote IP: ${remoteIP} --> ${origin}${url}`);
await next();
});
通常HTTP请求使用GET或者POST方法,GET请求的参数可以直接解析请求中字符串就可以获取。但是,POST的参数稍微麻烦一些,这里我们引入“koa-bodyparser”模块,使用它来解析POST的参数。
const bodyParser = require('koa-bodyparser');
// POST参数解析
app.use(bodyParser({
onerror: function (err, ctx) {
ctx.throw('body parse error', 422);
}
}));
给获取的参数预留一个统一个获取接口,为之后使用提供便利:
/**
* GET、POST参数解析,将参数统一放置在ctx.params中
*
*/
app.use(async (ctx, next) => {
if (ctx.request.method === 'GET') {
ctx.params = ctx.request.query;
} else if (ctx.request.method === 'POST') {
ctx.params = ctx.request.body;
}
await next();
});
“koa-router”模块可以帮助我们完成路由的解析,它提供了GET、POST、ALL等接口:
const KoaRouter = require('koa-router');
const route = new KoaRouter();
route.get('url',function handler(){});
app.use(route.routes()).use(route.allowedMethods());
当请求多时,每次都去写一次GET或POST是一件很繁琐的事情,所以,提供一个通过配置完成路由设置的架构。
将路由的URL,以处理方法的所在文件和方法名配置好,如下:
{
"port": 3000,
"https_port": 9909,
"get": [
{
"url": "/getUsers",
"file": "./interface/userinfo.js",
"func": "getUsers"
}
],
"post": [
{
"url": "/getUser",
"file": "./interface/userinfo.js",
"func": "getUser"
}
]
}
在app.js文件中一次性加载完所有预定义的路由,以及其处理方式:
/**
* 获取请求处理方法
*
* @param {String} path 文件路径
* @param {String} method 方法名
*/
function getRoutesHandle(path, method) {
var res = require(path);
var ret = res[method];
return ret;
}
// GET请求加载
getMethod.forEach(e => {
route.get(e.url, getRoutesHandle(e.file, e.func));
});
// POST请求加载
postMethod.forEach(e => {
route.post(e.url, getRoutesHandle(e.file, e.func));
});
实现思路:以“接口”为入口,再由接口去调用的“业务”。优点,所有接口(路由)写在配置文件中,一目了然。同时,需要修改路由时也很方便,不需要去各个文件中去查找(在Java后台中,当需要修改一个请求时,需要先找到请求所在controller层,比较麻烦)。
这里的日志使用的bunyan,需要自己稍微配置下日志的输出格式,还是比较方便的:
var bunyan = require('bunyan');
var config = require('./configReaderLib.js').loadLogConfig();
var Log = bunyan.createLogger({
src: true,
name: 'mine',
streams: [
{
type: 'rotating-file',
level: 'error',
path: 'log/log_error.log',// 日志输出到文件
period: '1d', // daily rotation
count: 3, // keep 3 back copies
},
{
type: 'rotating-file',
level: config.level === undefined ? 'info' : config.level,
path: 'log/log_info.log',// 日志输出到文件
period: '1d', // daily rotation
count: 3 // keep 3 back copies
},
{
level: 'debug',
stream: process.stdout
}
]
});
源码里面提供了MongoDB数据库的操作接口,有兴趣可以看下。