簡體   English   中英

如何在Node.js / Express.js中將多個請求分開

[英]How to keep multiple requests separate in Nodejs / Expressjs

我正在計算機上開發NodeJS / ExpressJS應用程序。 我的節點在本地運行。 我有一個單頁Web應用程序。 當需要信息時,它使用jQuery發出ajax請求。

問題是當我對多個不同的數據集有多個請求時。 Node / express將開始處理第一個請求,然后第二個請求在第一個請求完成之前進入,它從第一個請求發送對第二個請求的響應,而不是像預期的那樣將其發送到第一個請求。 如果我在我的應用程序中放置了一個暫停(使用警報),這會減慢它的速度,因此直到滿足第一個請求時才發送下一個請求,否則一切正常。

我不明白為什么會這樣。 我以為node / express應該能夠處理此問題並將請求分開。

另外,我在節點中收到“無法發送標頭后再設置標頭”錯誤,因為它顯然正在合並請求。

這是發生了什么

ajax request 1 -> server
ajax request 2 -> server
ajax request 3 -> server

server -> request1 ( no response )
server -> request2 ( request 1's data)
server -> request3 ( request 2's data)
server for request3 --> error: header's already sent

我正在運行Node 8.9.4和Express 4.16.2的Windows 10計算機上使用帶有jQuery 3.3.1的Google Chrome 63

我的解決方法是將ajax請求鏈接起來,以便在先前的請求收到服務器的響應之前,不調用每個請求。 但我不必這樣做...

這是相關的服務器代碼:

var mysql = require("mysql");
var express = require('express');
var app = express();

var DEBUG = true;

var request = null;
var response = null;

var currentDataRowParser = null;
var con = mysql.createConnection(config);

function ParseMySqlRowData(rowData)
{
    if (DEBUG) console.log("ParseMySqlRowData");

    return JSON.stringify(rowData);
}

var ParseMySqlRowsDatatoResponse = function (err, rows)
{
    if (DEBUG) console.log("ParseMySqlRowsDatatoResponse");

    var MySQLRows;

    try
    {
        if (!err)
        {
            MySQLRows = "[";
            for (var i = 0; i < rows.length; i++)
            {
                if (i > 0)
                    MySQLRows += ", ";

                MySQLRows += currentDataRowParser(rows[i]);
            }

            MySQLRows += "]";

            if (DEBUG) console.log("write rows");
            if (DEBUG) console.log(MySQLRows);
            response.send(MySQLRows);
            response.end();
        }
    }
    catch (ex)
    {
        if (DEBUG) console.log("ParseMySQLRowsDatatoResponse: ERROR");
        if (DEBUG) console.log(ex);
    }
};

var GetQueryData = function (query, dataRowParser, parseDataCallbackFunction)
{
    if (DEBUG) console.log("GetQueryData");

    currentDataRowParser = dataRowParser;

    if (parseDataCallbackFunction == null || parseDataCallbackFunction == undefined)
        parseDataCallbackFunction = ParseDataCallback;

    try
    {
        if (DEBUGQUERY)
        {
            console.log("before query");
            console.log(con.query(query, parseDataCallbackFunction));
            console.log("after query");
            console.log(query.sql);
            DEBUGQUERY = false;
        }
        else
        {
            con.query(query, parseDataCallbackFunction);
        }
    }
    catch (ex)
    {
        console.log(ex);
    }
};

app.post('/getdata', function(req, res)
{
    request = req;
    response = res;
    var query;
    switch (request.body.loaddata)
    {
    case "dataset1":
        query = "SELECT * FROM table1 WHERE key='" + request.body.key + "'";
        GetQueryData(query,ParseMySqlRowData,ParseMySqlRowsDatatoResponse);
        break;
    case "dataset2":
        query = "SELECT * FROM table2 WHERE key='" + request.body.key + "'";
        GetQueryData(query,ParseMySqlRowData,ParseMySqlRowsDatatoResponse);
        break;
    case "dataset3":
        query = "SELECT * FROM table3 WHERE key='" + request.body.key + "'";
        GetQueryData(query,ParseMySqlRowData,ParseMySqlRowsDatatoResponse);
        break;

    }
};

您不能將reqres存儲在全局或模塊級變量中。 當出現第二個請求時,它將立即覆蓋您的全局變量,並將混合各種請求的數據。

節點不分隔每個請求實例嗎?

是的,有一個單獨的請求實例,但沒有一個單獨的全局或模塊級名稱空間。 因此,當您將req分配到全局空間時,您將覆蓋前一個,然后您的代碼將使用錯誤的代碼。

將請求和響應作為全局變量非常有幫助。 否則,我將不得不在整個地方傳遞它們。

您必須將它們傳遞給需要它們的較低級別的函數。 這就是您將每個請求與其他請求分開的方式。 任何需要對reqres進行操作的函數都應傳遞這些變量,以便它確切知道要對哪個進行操作。

node.js具有共享的全局和模塊級名稱空間。 因此,所有同時進行的請求都使用相同的名稱空間。 僅應存儲的數據是您特別希望在請求之間共享的數據(例如,會話狀態)。 單獨的請求或響應對象不應存儲在共享變量中。


編碼您的代碼類型的更常見方法是調用GetQueryData()類的函數,並使其返回數據(可能通過GetQueryData() ),然后在原始請求處理程序中發送響應。 然后,您根本不必通過reqres向下傳遞多個級別。 您的助手功能只是獲取數據。 請求處理程序獲取數據,然后發送響應。 通常,這是對功能的更好封裝。


這是按照上述方式重組代碼的一種方法。

  1. GetQueryData()返回一個滿足數據的承諾
  2. ParseMySqlRowsData()僅返回解析結果,如果解析錯誤,則返回null
  3. app.post()只是通過諾言獲取數據,然后發送適當的響應。
  4. 有沒有全球性的reqres ,有沒有必要在任何地方通過他們。

這是代碼:

var mysql = require("mysql");
var express = require('express');
var app = express();

var DEBUG = true;

var currentDataRowParser = null;
var con = mysql.createConnection(config);

function ParseMySqlRowData(rowData) {
    if (DEBUG) console.log("ParseMySqlRowData");

    return JSON.stringify(rowData);
}

var ParseMySqlRowsData = function(err, rows) {
    if (DEBUG) console.log("ParseMySqlRowsDatatoResponse");

    var MySQLRows;

    try {
        if (!err) {
            MySQLRows = "[";
            for (var i = 0; i < rows.length; i++) {
                if (i > 0)
                    MySQLRows += ", ";

                MySQLRows += currentDataRowParser(rows[i]);
            }

            MySQLRows += "]";

            if (DEBUG) console.log("write rows");
            if (DEBUG) console.log(MySQLRows);
            return MySQLRows;
        }
    } catch (ex) {
        if (DEBUG) console.log("ParseMySQLRowsDatatoResponse: ERROR");
        if (DEBUG) console.log(ex);
        return null;
    }
};

var GetQueryData = function(query, dataRowParser, parseDataCallbackFunction) {
    return new Promise((resolve, reject) =>{
        if (DEBUG) console.log("GetQueryData");

        let currentDataRowParser = dataRowParser;

        if (parseDataCallbackFunction == null || parseDataCallbackFunction == undefined)
            parseDataCallbackFunction = ParseDataCallback;

        try {
            if (DEBUGQUERY) {
                console.log("before query");
                console.log(con.query(query, parseDataCallbackFunction));
                console.log("after query");
                console.log(query.sql);
                DEBUGQUERY = false;
            } else {
                con.query(query, function(err, rows) {
                    if (err) {
                        reject(err);
                    } else {
                        let result = parseDataCallbackFunction(rows);
                        if (result) {
                            resolve(result);
                        } else {
                            reject(new Error("ParseMySqlRowsData error"));
                        }
                    }
                });
            }
        } catch (ex) {
            console.log(ex);
            reject(new Error("GetQueryData error"));
        }
    });
};

app.post('/getdata', function(req, res) {
    var query;
    let p;
    switch (request.body.loaddata) {
        case "dataset1":
            query = "SELECT * FROM table1 WHERE key='" + request.body.key + "'";
            p = GetQueryData(query, ParseMySqlRowData, ParseMySqlRowsData);
            break;
        case "dataset2":
            query = "SELECT * FROM table2 WHERE key='" + request.body.key + "'";
            p = GetQueryData(query, ParseMySqlRowData, ParseMySqlRowsData);
            break;
        case "dataset3":
            query = "SELECT * FROM table3 WHERE key='" + request.body.key + "'";
            p = GetQueryData(query, ParseMySqlRowData, ParseMySqlRowsData);
            break;
        default:
            p = Promise.reject(new Error("Invalid request.body.loaddata"));
            break;
    }
    p.then(data => {
        res.send(data);
    }).catch(err => {
        console.log(err);
        res.sendStatus(500);
    });
};

PS我看到你仍然有一個模塊級別的變量,你不應該有: currentDataRowParser 需要通過將適當的解析器作為參數傳遞給ParseMySqlRowsData()ParseMySqlRowsData() ,而不使用模塊級共享變量。 我將把它作為練習內容供您解決。 在任何服務器編程中,特別是在node.js編程中,用於對特定請求狀態進行操作的共享變量都是一個壞主意。 將特定於請求的狀態保留在函數參數中或reqres對象本身上。 這樣一來,您就可以防止在處理一個請求時將一個請求的數據覆蓋另一個請求的數據。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM