软件测试工作过程中,我们很多时候都会需要一个模拟的服务器,用于对指定的请求作出我们所希望的响应。目前,有很多脚本语言都可以很方便的实现这个目的,而我由于工作需要,研究了下如何通过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,观察命令窗口返回响应:
浏览器页面则会弹出下载提示,如下:
点击打开即可看到返回的响应内容。
