第9章 XMLHttpRequest对象与nodeJS服务异步通信

9.1 设计需求

现提出如下设计需求:

  1. 设计并实现一个nodeJS后台服务,该服务可接收客户端发送的消息并告知客户端对消息进行了处理。
  2. 测试XMLHttpRequest对象向后台服务发送一条消息,后台将处理结果返回到前端。
  3. 测试XMLHttpRequest对象与后台通信的同步交互处理。
  4. 测试XMLHttpRequest对象与后台通信的异步交互处理。

9.2 设计分析

9.2.1 功能分析

测试用户期望功能描述如下:

  1. 测试XMLHttpRequest对象与后台通信的同步交互处理过程。
  2. 测试XMLHttpRequest对象与后台通信的异步交互处理过程。

测试用户期望功能用例图如图9.1所示。

用户测试功能用例建模
图9.1 用户期望功能用例图

9.2.2 核心业务数据流分析

测试用户核心业务数据流描述如下:

  1. 测试发出与后台同步或异步通信的交互请求。
  2. 后台NodeJS服务处理前端请求。
  3. 后台返回消息。
  4. 前端对后台返回的消息进行可视处理。

测试用户核心业务数据流描述如图9.2所示。

核心业务数据流图
图9.2 核心业务数据流图

9.2.3 文件包及文件设计分析

基于分层架构视角的文件包及文件设计如表9.1所述。

表9.1 文件包及文件设计
序号 包设计 文件设计
1 设计表示层包(包名:UI),抽象表示层文件容器。
  1. 设计界面主页文件main.js。
  2. 主页引入jquery框架(jquery.js),辅助表示层处理。
2 设计业务逻辑层包(包名:server),抽象业务层文件容器。
  1. 设计通用业务逻辑服务文件server.js,可引入专用服务模块。
  2. 设计当前模块专用业务逻辑服务文件server_09.js。

文件包设计及文件关系如图9.3所示。

文件包设计
图9.3 文件包设计及文件关系图

9.3 文件架构

文件架构如图9.4所示。

文件架构图
图9.4 文件架构图

说明:09文件夹与server.js位于同级文件夹。

文件及文件夹功能说明见表9.2。

表9.2 文件功能说明
序号 文件(夹)名 作用
1 main.html 实现人机交互的用户界面主页
2 Server.js NodeJS后台通用逻辑服务程序,动态引入Server_10.js模块。
3 Server_09.js 当前模块服务逻辑
4 UI 存放表示层文件的文件夹
5 server 存放业务逻辑层文件的文件夹
6 09 存放文件夹:UI、server。

9.4 代码实现

9.4.1 主页实现

主页main.html实现代码如下:

<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head>
    <meta charset="utf-8" />
    <title></title>
</head>
<body>
    <h2>XMLHttpRequest与node服务异步通信测试</h2>
    <hr />
    输入一条消息
    <input type="text" id="msg" />
    <hr />
    <input type="button" value="向服务器发送消息(同步通信)" onclick="send_MSG_1()" />
    <input type="button" value="向服务器发送消息(异步通信)" onclick="send_MSG_2()" />
</body>
</html>

<script type="text/javascript" src="jquery-1.11.1.js"></script>
<script type="text/javascript">
    //发送消息(异步)
    function send_MSG_T() {
        var xhttp = new XMLHttpRequest();
        var msg = $("#msg").val();
        xhttp.open("post", "http://localhost:1017/msg_Server_09?message=" + msg, true);
        xhttp.onreadystatechange = function () {
            if (xhttp.status == 200 && xhttp.readyState == 4) {
                alert("2、测试中:" + xhttp.responseText);
            }
        }
        xhttp.send();
    }

    //发送消息(同步)
    function send_MSG_F() {
        var xhttp = new XMLHttpRequest();
        var msg = $("#msg").val();
        xhttp.open("post", "http://localhost:1017/msg_Server_09?message=" + msg, false);
        xhttp.onreadystatechange = function () {
            if (xhttp.status == 200 && xhttp.readyState == 4) {
                alert("2、测试中:" + xhttp.responseText);
            }
        }
        xhttp.send();
    }

    //同步测试
    function send_MSG_1() {
        alert("1、测试前");
        send_MSG_F();
        alert("3、测试后");
    }

    //异步测试
    function send_MSG_2() {
        alert("1、测试前");
        send_MSG_T();
        alert("3、测试后");
    }
</script>

9.4.2 后台主服务程序实现

Server.js实现代码:

const http_obj = require("http");//用于产生服务对象
const fs_obj = require("fs");//读写后台文件
const url_tran_obj = require("url");//解释URL

const server = http_obj.createServer(function (request, response) {
    //请求路径处理
    var req_str = decodeURI(request.url);
    var req_head;
    var POS = req_str.indexOf("?");
    if (POS == -1) { req_head = req_str; }
    else { req_head = req_str.substring(0, POS); }

    //设置查询对象
    var url_obj = url_tran_obj.parse(req_str, true);
    var Q_obj = url_obj.query;

    //响应头设置
    response.setHeader("Content-Type", "text/plain;charset=utf-8");
    response.setHeader("Access-Control-Allow-Origin", "*");//实现跨域访问
    response.writeHead(200);

    //%%%%%%%%%%%%%------------begin your code
    //初始化所有服务对象
    require("./09/server/server_09.js")(request, response, req_head, Q_obj);
    //%%%%%%%%%%%%%--------------end your code
});

server.listen(1017);
console.log("Server is running at port 1017...");

9.4.3 后台子服务程序实现

Server_09.js实现代码:

function server_09(request, response, req_head, Q_obj) {
    if (req_head == "/msg_Server_09") {
        //消息服务测试
        response.end("这里是火星,已收到来自地球的问候:\n" + Q_obj["message"]);
    }
}

module.exports = server_09;

9.4.4 实现效果

在如图9.4所示的界面中输入"test"字样、若点击"向服务器发送消息(同步通信)"按钮,则依次弹出的消息如图9.5、9.6、9.7所示。

主界面
图9.5 主界面
同步通信第1次弹出消息
图9.5 同步通信第1次弹出消息
同步通信第2次弹出消息
图9.6 同步通信第2次弹出消息
同步通信第3次弹出消息
图9.7 同步通信第3次弹出消息

若点击"向服务器发送消息(异步通信)"按钮,则依次弹出的消息如图9.8、9.9、9.10所示。

异步通信第1次弹出消息
图9.8 异步通信第1次弹出消息
异步通信第2次弹出消息
图9.9 异步通信第2次弹出消息
异步通信第3次弹出消息
图9.10 异步通信第3次弹出消息

9.5 问题思考

问题提出:实现XMLHttpRequest对象与nodeJS服务异步通信的核心编程步骤是什么?

问题思考:

总结为关键四步曲。

  1. 建对象:新建XMLHttpRequest对象(XMLHttpRequest()构造函数实现)。
  2. 定参数:定义open()函数的参数(3参数设定:post、http、ture)。
  3. 写事件:重写onreadystatechange事件(2状态判定:status、readyState)。
  4. 发请求:发送请求(send()实现)。

9.6 仿真实训

给定nodeJS服务器框架如下:

// JavaScript source code
//------------请将这个文件保存为Unicode格式,否则麻烦大大的----------
const http_obj = require("http");//用于产生服务对象
const fs_obj = require("fs");//读写后台文件
const url_tran_obj = require("url");//解释URL

const server = http_obj.createServer(function (request, response) {
    //请求路径处理
    var req_str = decodeURI(request.url);
    var req_head;
    var POS = req_str.indexOf("?");
    if (POS == -1) { req_head = req_str; }
    else { req_head = req_str.substring(0, POS); }

    //设置查询对象
    var url_obj = url_tran_obj.parse(req_str, true);
    var Q_obj = url_obj.query;

    //响应头设置
    response.setHeader("Content-Type", "text/plain;charset=utf-8");
    response.setHeader("Access-Control-Allow-Origin", "*");//实现跨域访问
    response.writeHead(200);

    //%%%%%%%%%%%%%------------begin your code
    if (req_head == "/server_for_MSG") {
        //消息服务测试
        response.end("这里nodeJS后台服务器,已收到来自前端的问候:\n" + Q_obj["new_MSG"]);
    }
    //%%%%%%%%%%%%%--------------end your code
});

server.listen(1008);
console.log("Server is running at port 1008...");

现提出如下设计需求:

  1. 测试XMLHttpRequest对象向后台服务发送一条消息,后台将处理结果返回到前端。
  2. 测试XMLHttpRequest对象与后台通信的同步交互处理。
  3. 测试XMLHttpRequest对象与后台通信的异步交互处理。