软件测试工作过程中,我们很多时候都会需要一个模拟的服务器,用于对指定的请求作出我们所希望的响应。目前,有很多脚本语言都可以很方便的实现这个目的,而我由于工作需要,研究了下如何通过nodejs实现一个模拟的http服务器,对来自客户端的请求作出各种判断,处理,统计等操作。下面对我的实现结果做一个记录:
首先,讲一下我的大体思路。我将模拟http服务器代码分成了三个部分:启动服务、路由选择和响应处理。为了使得脚本结构明晰,简单,我创建了三个.js脚本,分别为index.js、server.js和route.js。其中index.js作为服务启动入口,该脚本只需调用方法启动服务器进程即可,而具体的启动方法则是在server.js中编写,并且整个实现中需要使用到的方法,都会定义到该脚本,当然,路由选择方法单独写在route.js脚本里。这样,脚本结构,功能就都很明确了。
接下来,就是具体的代码内容,先看server.js:
//引入脚本中需要使用到的相关模块
var http = require("http");
var url = require('url');
var fs = require('fs');
const querystring = require('querystring');
// 定义脚本变量
var num_ok = 0;
var num_err = 0;
//创建函数server_start()
function server_start(route,handle){
// 创建方法onRequest(),主要对请求以及响应进行处理
function onRequest(req,res){
//显示接收的请求url
console.log(req.url);
//显示请求的头域,由于格式为json,所以使用JSON.stringify()方法将json体转为String
console.log(JSON.stringify(req.headers));
//设置请求格式为utf-8
req.setEncoding('utf-8');
//监听请求body数据,触发匿名方法
req.on('data',function(chunk){
/* 命令界面显示请求body:若为JSON格式,使用JSON.stringify()方法转为String,若为encode格式,则使用
querystring.unescape()方法解码 */
console.log(JSON.stringify(chunk));
/* 对body体中某属性作操作:若为JSON格式,使用JSON.parse()方法将对象转换为字典;若为encode格式字符串,
则使用querystring.parse(querrystring.unescape())方法将对象转换为字典 */
if(JSON.parse(chunk).duration > 0 ){
// 将输出赋予变量str_ok,'\r\n'用于windows表示换行,linux为'\n'
var str_ok = '通话时间为:'+JSON.parse(chunk).duration+',主叫号码为:'+JSON.parse(chunk).callerNum+'\r\n';
// 将变量输出到命令界面
console.log(str_ok);
/* 将变量写入文件file_ok:若是想每次调用时覆盖之前内容,则使用fs.writeFile()方法;若想保留之前的内容
则使用fs.appendFile()方法。file_ok.txt文件若已存在,则直接写入,若不存在,则自动创建 */
fs.appendFile('./file_ok.txt',str_ok,function(err){
console.log(err);
});
// 统计接收消息中duration不为0的消息数
num_ok++;
}else{
var str_err = '主叫号码为:'+JSON.parse(chunk).callerNum+'通话时长为0.\r\n';
console.log(str_err);
fs.appendFile('./file_err.txt',str_err,function(err){
console.log(err);
})
num_err++;
};
console.log('正常请求数为:'+num_ok+',异常请求数为:'+num_err);
});
// 获取url中pathname部分用于请求判断,然后进行分类处理
var pathname = url.parse(req.url).pathname;
// 使用形参带入的方法route()进行处理请求,根据不同的输入返回不同的响应
route(pathname,handle,res);
}
// 调用http类的createServer()方法创建服务器,并监听端口8888
http.createServer(onRequest).listen(8888);
// 启动服务时界面显示http server is running on 127.0.0.1:8888
console.log('http server is running on 127.0.0.1:8888');
}
//定义方法请求处理方法bind()
function bind(res){
//命令界面显示返回响应内容
console.log('{"resultcode":"0","resultdesc":"Success"}');
//设置返回响应头域
res.writeHeader(200,{'Content-Type':'application/json;charset=UTF-8'});
// 设置返回响应body
res.write('{"resultcode":"0","resultdesc":"Success"}');
// 结束响应输入,若无此命令,响应不会结束,导致每个请求处理时间很长
res.end();
}
//使用exports方法导出server_start()和bind()方法,其他文件引入此文件后,即可使用exports后面方法别名类调用这些导出来的方法
exports.func = server_start;
exports.bind = bind;
route.js:
//定义方法route(),使得不同请求选择不同的方法处理,返回需要的各类响应
function route(pathname,handle,response){
// 判断字典中key为pathname的value是否为方法,且判断pathname的值 : nodejs中逻辑运算符 ! && ||
if(typeof handle[pathname] === 'function' && pathname == '/bind' ) {
// 调用字典中value代表的方法,并传入参数response
handle[pathname](response);
} else {
// 不满足要求时执行异常处理
console.log('Requestrian is not execpted!');
response.writeHeader(400,{'Content-Type':'application/json;charset=UTF-8'});
response.write('{"resultcode":"00001","resultdesc":"Request is not execpted!"}');
response.end();
}
}
// 导出方法route
exports.route = route;
index.js:
//引入自定义模块,引入路径中可以带入.js也可不带 :注意,想引入自定义方法,一是引入模块,二是模块中需要exports方法
var server = require('./server');
var route = require('./route');
// 定义字典,key为请求url中pathname部分,value值为方法,这样便可将请求和处理方法一一对应了
var handle = {};
handle["/bind"] = server.bind;
// 调用自定义方法func启动服务,此处的func为自定义方法server_start的别名
server.func(route.route,handle);
以上就是所有的代码的,当然只是做了简单的接收,选择,判断,处理等操作。下面我们启动服务器,做一个简单的测试:
启动服务后,可以通过浏览器窗口输入http://localhost:8888/bind,观察命令窗口返回响应:
浏览器页面则会弹出下载提示,如下:
点击打开即可看到返回的响应内容。