javascript 学习第二章 语言核心-服务器端JavaScript
阿明
撰写于 2024年 05月 18 日

12.服务器端JavaScript

12.1用rhino脚本化JavaScript

​ 官网:https://github.com/mozilla/rhino

https://github.com/mozilla/rhino/releases/tag/Rhino1_7_11_RC2_Release

​ "C:\Program Files\Java\jdk1.8.0_201\bin\java.exe" -jar rhino-1.7.11-RC2.jar download.js

importPackage(javax.swing);
importClass(javax.swing.border.EmptyBorder);
importClass(java.awt.event.ActionListener);
importClass(java.net.URL);
importClass(java.io.FileOutputStream);
importClass(java.lang.Thread);

// 创建一些GUI小部件(widget)
var frame = new JFrame("Rhino URL Fetcher");
var urlField = new JTextField(30);
var button = new JButton("Download");
var fileChooser = new JFileChooser();
var row = Box.createHorizontalBox();
var col = Box.createVerticalBox();
var padding = new EmptyBorder(3, 3, 3, 3);

// 把它们组装一起并显示这个GUI
row.add(urlField);
row.add(button);
col.add(row);
frame.add(col);
row.setBorder(padding);
frame.pack();
frame.setVisible(true);

// 当窗体中发生任何事件都会调用这个函数
frame.addWindowListener(new java.awt.event.WindowAdapter() {
    windowClosing: function (e) {
        java.lang.System.exit(0);
    }
});

// 当用户单击按钮时,调用这个函数
button.addActionListener(function () {
    try {
        // 创建java.net.URL表示源URL
        var url = new URL(urlField.getText());
        // 告诉用户选择保存URL内容的文件
        var response = fileChooser.showSaveDialog(frame);
        // 如果单击Cancel按钮,立即退出
        if (response != JFileChooser.APPROVE_OPTION) return;
        // 否则,获取java.io.File表示目标文件
        var file = fileChooser.getSelectedFile();
        // 现在启动一个新线程下载URL
        new Thread(function () { download(url, file); }).start();
    } catch (e) {
        // 如果出现错误,显示一个对话框
        JOptionPane.showMessageDialog(frame, e.message, "Exception", JOptionPane.ERROR_MESSAGE);
    }
});

// 使用java.io.File等把内容保存到一个文件中
// 使用java.net.URL等下载URL的内容,在JProgressBar组件中显示下载进度
// 这将在一个新线程中调用
function download(url, file) {
    try {
        // 每次下载一个URL时,我们会添加一个新的数据行到窗体中
        // 数据行中会显示URL、文件名和下载进度
        var row = Box.createHorizontalBox();
        row.setBorder(padding);
        var label = url.toString() + ":";
        row.add(new JLabel(label));
        var bar = new JProgressBar(0, 100);
        bar.setStringPainted(true);
        bar.setString(file.toString());
        row.add(bar);
        col.add(row);
        frame.pack();

        // 我们不知道URL的大小,所以进度条是动画
        bar.setIndeterminate(true);
        var conn = url.openConnection();
        conn.connect();
        var len = conn.getContentLength();

        if (len > 0) {
            // 如果能得到URL长度就设置
            bar.setMaximum(len);
            bar.setIndeterminate(false);
        }

        // 下载的百分比
        var input = conn.getInputStream();
        var output = new FileOutputStream(file); // 把字节写入文件
        var buffer = java.lang.reflect.Array.newInstance(java.lang.Byte.TYPE, 4096);
        var num;
        while ((num = input.read(buffer)) != -1) { // 读取然后循环至EOF
            output.write(buffer, 0, num); // 把字节写入文件
            bar.setValue(bar.getValue() + num); // 更新进度条
        }
        output.close();
        input.close();
    } catch (e) { // 如果发生错误,在进度条上显示错误
        if (bar) {
            bar.setIndeterminate(false);
            bar.setString(e.toString());
        }
    }
}

12.2 用node实现异步i/o

12.2.1 node示例:http服务器

// 首先,加载所有要用的模块
var http = require('http');
var fs = require('fs');
var url = require('url');

// 创建新的HTTP服务器
var server = new http.Server();
server.listen(8000); // 在端口8000上运行它

// Node使用"on()"方法注册事件处理程序,当服务器得到新请求,则运行函数处理它
server.on("request", function(request, response) {
    // 解析请求的URL
    var parsedUrl = url.parse(request.url, true);

    // 特殊URL会让服务器在发送响应前先等待,此处用于模拟缓慢的网络连接
    if (parsedUrl.pathname === "/test/delay") {
        // 使用查询字符串来获取延迟时长,或者2000毫秒
        var delay = parseInt(parsedUrl.query.delay) || 2000;
        // 设置响应状态码和头
        response.writeHead(200, {"Content-Type": "text/plain; charset=UTF-8"});
        // 立即开始编写响应主体
        response.write("sleeping for " + delay + " milliseconds...");
        // 在之后调用的另一个函数中完成响应
        setTimeout(function() {
            response.write("done.");
            response.end();
        }, delay);

    // 若请求是"/test/mirror”,则原文返回它
    // 当需要看到这个请求头和主体时,会很有用
    } else if (parsedUrl.pathname === "/test/mirror") {
        // 响应状态和头
        response.writeHead(200, {"Content-Type": "text/plain; charset=UTF-8"});
        // 用请求的内容开始编写响应主体
        response.write(request.method + " " + request.url + " HTTP/" + request.httpVersion + "\r\n");
        // 所有的请求头
        for (var h in request.headers) {
            response.write(h + ": " + request.headers[h] + "\r\n");
        }
        response.write("\r\n"); // 使用额外的空白行来结束头
        // 把请求体写回响应
        request.pipe(response);

    // 否则,处理来自本地目录的文件
    } else {
        // 获取本地文件名,基于其扩展名推测内容类型
        var filename = parsedUrl.pathname.substring(1); // 去掉前导"/"
        var type;
        switch (filename.substring(filename.lastIndexOf(".") + 1)) { // 扩展名
            case "html":
            case "htm":
                type = "text/html; charset=UTF-8";
                break;
            case "js":
                type = "application/javascript; charset=UTF-8";
                break;
            case "css":
                type = "text/css; charset=UTF-8";
                break;
            case "txt":
                type = "text/plain; charset=UTF-8";
                break;
            case "manifest":
                type = "text/cache-manifest; charset=UTF-8";
                break;
            default:
                type = "application/octet-stream";
                break;
        }

        // 异步读取文件,并将内容作为单独的数据块传给回调函数
        // 对于确实很大的文件,使用流API fs.createReadStream()更好
        fs.readFile(filename, function(err, content) {
            if (err) { // 如果由于某些原因无法读取该文件
                response.writeHead(404, { "Content-Type": "text/plain; charset=UTF-8" }); // 发送404未找到状态码
                response.write(err.message); // 简单的错误消息主体
                response.end(); // 完成
            } else { // 否则,若读取文件成功
                response.writeHead(200, { "Content-Type": type }); // 设置状态码和MIME类型
                response.write(content); // 把文件内容作为响应主体发送
                response.end(); // 完成
            }
        });
    }
});

运行代码

  1. 确保你已经安装了Node.js:可以通过以下命令检查:
node -v

如果没有安装,可以从Node.js官网下载并安装。

  1. 保存代码:将上述修正后的代码保存到一个文件中,例如server.js
  2. 运行服务器:在终端中导航到包含server.js的目录,然后运行以下命令:
node server.js
  1. 测试服务器

    • 打开浏览器并访问http://localhost:8000/test/delay?delay=3000来测试延迟响应。
    • 访问http://localhost:8000/test/mirror来测试镜像请求。
    • 访问http://localhost:8000/filename来测试文件服务(替换filename为实际存在的文件名)。

12.2.2 node示例:http客户端工具模块

​ app.js

const httputils = require('./httputils'); // 引入httputils模块

// 测试GET请求
const url = 'http://www.example.com'; // 替换为你要请求的URL
httputils.get(url, function(status, headers, body) {
    console.log('GET Request:');
    console.log('Status:', status);
    console.log('Headers:', headers);
    console.log('Body:', body);
});

// 测试POST请求
const postData = { key1: 'value1', key2: 'value2' }; // 替换为你要发送的数据
httputils.post(url, postData, function(status, headers, body) {
    console.log('POST Request:');
    console.log('Status:', status);
    console.log('Headers:', headers);
    console.log('Body:', body);
});

​ httputils.js

const http = require('http');
const url = require('url');
const querystring = require('querystring');

// 实现一个异步HTTP GET请求,并将HTTP状态、头和响应主体传递给指定的回调函数
exports.get = function(urlString, callback) {
    // 解析URL,获取所需的信息
    const parsedUrl = url.parse(urlString);
    const hostname = parsedUrl.hostname;
    const port = parsedUrl.port || 80;
    let path = parsedUrl.pathname;
    const query = parsedUrl.query;
    if (query) path += '?' + query;

    // 实现一个简单的GET请求
    const options = {
        hostname: hostname,
        port: port,
        path: path,
        method: 'GET',
        headers: {
            "Host": hostname // Request headers
        }
    };

    const req = http.request(options, function(response) {
        response.setEncoding("utf8"); // 设置编码,使返回的主体成为文本而非字节
        let body = ""; // 一旦响应主体达到,保存它
        response.on("data", function(chunk) {
            body += chunk;
        });
        response.on("end", function() {
            if (callback) callback(response.statusCode, response.headers, body);
        });
    });

    req.on("error", function(e) {
        console.error(`Problem with request: ${e.message}`);
    });

    req.end();
};

// 以数据作为请求主体的简单HTTP POST请求
exports.post = function(urlString, data, callback) {
    // 解析URL,获取所需的信息
    const parsedUrl = url.parse(urlString);
    const hostname = parsedUrl.hostname;
    const port = parsedUrl.port || 80;
    let path = parsedUrl.pathname;
    const query = parsedUrl.query;
    if (query) path += '?' + query;

    // 判断将要作为请求主体发送的数据类型
    let type;
    if (data == null) {
        data = '';
        type = "text/plain;charset=UTF-8";
    } else if (data instanceof Buffer) {
        // 二进制数据
        type = "application/octet-stream";
    } else if (typeof data === "string") {
        // 字符串数据
        type = "text/plain;charset=UTF-8";
    } else if (typeof data === "object") {
        // 名/值对
        data = querystring.stringify(data);
        type = "application/x-www-form-urlencoded";
    }

    // 生成POST请求,其中包括请求主体
    const options = {
        hostname: hostname,
        port: port,
        path: path,
        method: 'POST',
        headers: {
            "Host": hostname,
            "Content-Type": type,
            "Content-Length": Buffer.byteLength(data)
        }
    };

    const req = http.request(options, function(response) {
        response.setEncoding("utf8"); // 假设它是文本
        let body = ""; // 用于保存响应主体
        response.on("data", function(chunk) {
            body += chunk;
        });
        response.on("end", function() {
            // 完成后,调用回调函数
            if (callback) callback(response.statusCode, response.headers, body);
        });
    });

    req.on("error", function(e) {
        console.error(`Problem with request: ${e.message}`);
    });

    req.write(data); // 发送请求主体
    req.end();
};
node app.js
  1. GET 请求

    • httputils.get 方法发起一个 GET 请求。
    • 它解析 URL,创建一个 HTTP 请求,并在接收到响应时调用回调函数。
    • 回调函数打印出状态码、响应头和响应主体。
  2. POST 请求

    • httputils.post 方法发起一个 POST 请求。
    • 它解析 URL,确定请求主体的数据类型,创建一个 HTTP 请求,并在接收到响应时调用回调函数。
    • 回调函数打印出状态码、响应头和响应主体。

    这样,你就可以使用 httputils.js 模块来发起异步的 HTTP GET 和 POST 请求,并处理服务器的

javascript 学习第二章 语言核心-服务器端JavaScript

12.服务器端JavaScript

12.1用rhino脚本化JavaScript

​ 官网:https://github.com/mozilla/rhino

https://github.com/mozilla/rhino/releases/tag/Rhino1_7_11_RC2_Release

​ "C:\Program Files\Java\jdk1.8.0_201\bin\java.exe" -jar rhino-1.7.11-RC2.jar download.js

importPackage(javax.swing);
importClass(javax.swing.border.EmptyBorder);
importClass(java.awt.event.ActionListener);
importClass(java.net.URL);
importClass(java.io.FileOutputStream);
importClass(java.lang.Thread);

// 创建一些GUI小部件(widget)
var frame = new JFrame("Rhino URL Fetcher");
var urlField = new JTextField(30);
var button = new JButton("Download");
var fileChooser = new JFileChooser();
var row = Box.createHorizontalBox();
var col = Box.createVerticalBox();
var padding = new EmptyBorder(3, 3, 3, 3);

// 把它们组装一起并显示这个GUI
row.add(urlField);
row.add(button);
col.add(row);
frame.add(col);
row.setBorder(padding);
frame.pack();
frame.setVisible(true);

// 当窗体中发生任何事件都会调用这个函数
frame.addWindowListener(new java.awt.event.WindowAdapter() {
    windowClosing: function (e) {
        java.lang.System.exit(0);
    }
});

// 当用户单击按钮时,调用这个函数
button.addActionListener(function () {
    try {
        // 创建java.net.URL表示源URL
        var url = new URL(urlField.getText());
        // 告诉用户选择保存URL内容的文件
        var response = fileChooser.showSaveDialog(frame);
        // 如果单击Cancel按钮,立即退出
        if (response != JFileChooser.APPROVE_OPTION) return;
        // 否则,获取java.io.File表示目标文件
        var file = fileChooser.getSelectedFile();
        // 现在启动一个新线程下载URL
        new Thread(function () { download(url, file); }).start();
    } catch (e) {
        // 如果出现错误,显示一个对话框
        JOptionPane.showMessageDialog(frame, e.message, "Exception", JOptionPane.ERROR_MESSAGE);
    }
});

// 使用java.io.File等把内容保存到一个文件中
// 使用java.net.URL等下载URL的内容,在JProgressBar组件中显示下载进度
// 这将在一个新线程中调用
function download(url, file) {
    try {
        // 每次下载一个URL时,我们会添加一个新的数据行到窗体中
        // 数据行中会显示URL、文件名和下载进度
        var row = Box.createHorizontalBox();
        row.setBorder(padding);
        var label = url.toString() + ":";
        row.add(new JLabel(label));
        var bar = new JProgressBar(0, 100);
        bar.setStringPainted(true);
        bar.setString(file.toString());
        row.add(bar);
        col.add(row);
        frame.pack();

        // 我们不知道URL的大小,所以进度条是动画
        bar.setIndeterminate(true);
        var conn = url.openConnection();
        conn.connect();
        var len = conn.getContentLength();

        if (len > 0) {
            // 如果能得到URL长度就设置
            bar.setMaximum(len);
            bar.setIndeterminate(false);
        }

        // 下载的百分比
        var input = conn.getInputStream();
        var output = new FileOutputStream(file); // 把字节写入文件
        var buffer = java.lang.reflect.Array.newInstance(java.lang.Byte.TYPE, 4096);
        var num;
        while ((num = input.read(buffer)) != -1) { // 读取然后循环至EOF
            output.write(buffer, 0, num); // 把字节写入文件
            bar.setValue(bar.getValue() + num); // 更新进度条
        }
        output.close();
        input.close();
    } catch (e) { // 如果发生错误,在进度条上显示错误
        if (bar) {
            bar.setIndeterminate(false);
            bar.setString(e.toString());
        }
    }
}

12.2 用node实现异步i/o

12.2.1 node示例:http服务器

// 首先,加载所有要用的模块
var http = require('http');
var fs = require('fs');
var url = require('url');

// 创建新的HTTP服务器
var server = new http.Server();
server.listen(8000); // 在端口8000上运行它

// Node使用"on()"方法注册事件处理程序,当服务器得到新请求,则运行函数处理它
server.on("request", function(request, response) {
    // 解析请求的URL
    var parsedUrl = url.parse(request.url, true);

    // 特殊URL会让服务器在发送响应前先等待,此处用于模拟缓慢的网络连接
    if (parsedUrl.pathname === "/test/delay") {
        // 使用查询字符串来获取延迟时长,或者2000毫秒
        var delay = parseInt(parsedUrl.query.delay) || 2000;
        // 设置响应状态码和头
        response.writeHead(200, {"Content-Type": "text/plain; charset=UTF-8"});
        // 立即开始编写响应主体
        response.write("sleeping for " + delay + " milliseconds...");
        // 在之后调用的另一个函数中完成响应
        setTimeout(function() {
            response.write("done.");
            response.end();
        }, delay);

    // 若请求是"/test/mirror”,则原文返回它
    // 当需要看到这个请求头和主体时,会很有用
    } else if (parsedUrl.pathname === "/test/mirror") {
        // 响应状态和头
        response.writeHead(200, {"Content-Type": "text/plain; charset=UTF-8"});
        // 用请求的内容开始编写响应主体
        response.write(request.method + " " + request.url + " HTTP/" + request.httpVersion + "\r\n");
        // 所有的请求头
        for (var h in request.headers) {
            response.write(h + ": " + request.headers[h] + "\r\n");
        }
        response.write("\r\n"); // 使用额外的空白行来结束头
        // 把请求体写回响应
        request.pipe(response);

    // 否则,处理来自本地目录的文件
    } else {
        // 获取本地文件名,基于其扩展名推测内容类型
        var filename = parsedUrl.pathname.substring(1); // 去掉前导"/"
        var type;
        switch (filename.substring(filename.lastIndexOf(".") + 1)) { // 扩展名
            case "html":
            case "htm":
                type = "text/html; charset=UTF-8";
                break;
            case "js":
                type = "application/javascript; charset=UTF-8";
                break;
            case "css":
                type = "text/css; charset=UTF-8";
                break;
            case "txt":
                type = "text/plain; charset=UTF-8";
                break;
            case "manifest":
                type = "text/cache-manifest; charset=UTF-8";
                break;
            default:
                type = "application/octet-stream";
                break;
        }

        // 异步读取文件,并将内容作为单独的数据块传给回调函数
        // 对于确实很大的文件,使用流API fs.createReadStream()更好
        fs.readFile(filename, function(err, content) {
            if (err) { // 如果由于某些原因无法读取该文件
                response.writeHead(404, { "Content-Type": "text/plain; charset=UTF-8" }); // 发送404未找到状态码
                response.write(err.message); // 简单的错误消息主体
                response.end(); // 完成
            } else { // 否则,若读取文件成功
                response.writeHead(200, { "Content-Type": type }); // 设置状态码和MIME类型
                response.write(content); // 把文件内容作为响应主体发送
                response.end(); // 完成
            }
        });
    }
});

运行代码

  1. 确保你已经安装了Node.js:可以通过以下命令检查:
node -v

如果没有安装,可以从Node.js官网下载并安装。

  1. 保存代码:将上述修正后的代码保存到一个文件中,例如server.js
  2. 运行服务器:在终端中导航到包含server.js的目录,然后运行以下命令:
node server.js
  1. 测试服务器

    • 打开浏览器并访问http://localhost:8000/test/delay?delay=3000来测试延迟响应。
    • 访问http://localhost:8000/test/mirror来测试镜像请求。
    • 访问http://localhost:8000/filename来测试文件服务(替换filename为实际存在的文件名)。

12.2.2 node示例:http客户端工具模块

​ app.js

const httputils = require('./httputils'); // 引入httputils模块

// 测试GET请求
const url = 'http://www.example.com'; // 替换为你要请求的URL
httputils.get(url, function(status, headers, body) {
    console.log('GET Request:');
    console.log('Status:', status);
    console.log('Headers:', headers);
    console.log('Body:', body);
});

// 测试POST请求
const postData = { key1: 'value1', key2: 'value2' }; // 替换为你要发送的数据
httputils.post(url, postData, function(status, headers, body) {
    console.log('POST Request:');
    console.log('Status:', status);
    console.log('Headers:', headers);
    console.log('Body:', body);
});

​ httputils.js

const http = require('http');
const url = require('url');
const querystring = require('querystring');

// 实现一个异步HTTP GET请求,并将HTTP状态、头和响应主体传递给指定的回调函数
exports.get = function(urlString, callback) {
    // 解析URL,获取所需的信息
    const parsedUrl = url.parse(urlString);
    const hostname = parsedUrl.hostname;
    const port = parsedUrl.port || 80;
    let path = parsedUrl.pathname;
    const query = parsedUrl.query;
    if (query) path += '?' + query;

    // 实现一个简单的GET请求
    const options = {
        hostname: hostname,
        port: port,
        path: path,
        method: 'GET',
        headers: {
            "Host": hostname // Request headers
        }
    };

    const req = http.request(options, function(response) {
        response.setEncoding("utf8"); // 设置编码,使返回的主体成为文本而非字节
        let body = ""; // 一旦响应主体达到,保存它
        response.on("data", function(chunk) {
            body += chunk;
        });
        response.on("end", function() {
            if (callback) callback(response.statusCode, response.headers, body);
        });
    });

    req.on("error", function(e) {
        console.error(`Problem with request: ${e.message}`);
    });

    req.end();
};

// 以数据作为请求主体的简单HTTP POST请求
exports.post = function(urlString, data, callback) {
    // 解析URL,获取所需的信息
    const parsedUrl = url.parse(urlString);
    const hostname = parsedUrl.hostname;
    const port = parsedUrl.port || 80;
    let path = parsedUrl.pathname;
    const query = parsedUrl.query;
    if (query) path += '?' + query;

    // 判断将要作为请求主体发送的数据类型
    let type;
    if (data == null) {
        data = '';
        type = "text/plain;charset=UTF-8";
    } else if (data instanceof Buffer) {
        // 二进制数据
        type = "application/octet-stream";
    } else if (typeof data === "string") {
        // 字符串数据
        type = "text/plain;charset=UTF-8";
    } else if (typeof data === "object") {
        // 名/值对
        data = querystring.stringify(data);
        type = "application/x-www-form-urlencoded";
    }

    // 生成POST请求,其中包括请求主体
    const options = {
        hostname: hostname,
        port: port,
        path: path,
        method: 'POST',
        headers: {
            "Host": hostname,
            "Content-Type": type,
            "Content-Length": Buffer.byteLength(data)
        }
    };

    const req = http.request(options, function(response) {
        response.setEncoding("utf8"); // 假设它是文本
        let body = ""; // 用于保存响应主体
        response.on("data", function(chunk) {
            body += chunk;
        });
        response.on("end", function() {
            // 完成后,调用回调函数
            if (callback) callback(response.statusCode, response.headers, body);
        });
    });

    req.on("error", function(e) {
        console.error(`Problem with request: ${e.message}`);
    });

    req.write(data); // 发送请求主体
    req.end();
};
node app.js
  1. GET 请求

    • httputils.get 方法发起一个 GET 请求。
    • 它解析 URL,创建一个 HTTP 请求,并在接收到响应时调用回调函数。
    • 回调函数打印出状态码、响应头和响应主体。
  2. POST 请求

    • httputils.post 方法发起一个 POST 请求。
    • 它解析 URL,确定请求主体的数据类型,创建一个 HTTP 请求,并在接收到响应时调用回调函数。
    • 回调函数打印出状态码、响应头和响应主体。

    这样,你就可以使用 httputils.js 模块来发起异步的 HTTP GET 和 POST 请求,并处理服务器的


赞 (0)

评论区(暂无评论)

啊哦,评论功能已关闭~