简体   繁体   English

如何在Node.js / Express.js中将多个请求分开

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

I am developing a NodeJS / ExpressJS application on my computer. 我正在计算机上开发NodeJS / ExpressJS应用程序。 I have node running locally. 我的节点在本地运行。 I have a single page web app. 我有一个单页Web应用程序。 When it needs information it makes ajax requests with jQuery. 当需要信息时,它使用jQuery发出ajax请求。

The problem is when I have multiple requests for different sets of data. 问题是当我对多个不同的数据集有多个请求时。 Node/express will start processing the first request and then the second request comes in before the first request has been fulfilled, it sends the response to the second request from the first request instead of sending it to the first request like it is suppose to. Node / express将开始处理第一个请求,然后第二个请求在第一个请求完成之前进入,它从第一个请求发送对第二个请求的响应,而不是像预期的那样将其发送到第一个请求。 If I put a pause ( using an alert ) in my app so that is slows it down so the next request doesn't get sent until the first request was fulfilled, everything works fine. 如果我在我的应用程序中放置了一个暂停(使用警报),这会减慢它的速度,因此直到满足第一个请求时才发送下一个请求,否则一切正常。

I don't understand why this is happening. 我不明白为什么会这样。 I thought node / express was suppose to be able to handle this and keep the requests separate. 我以为node / express应该能够处理此问题并将请求分开。

Also, I get a "Can't set headers after they are sent" error in node because it is apparently merging the requests.... 另外,我在节点中收到“无法发送标头后再设置标头”错误,因为它显然正在合并请求。

Here is what happens 这是发生了什么

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

I am using Google Chrome 63 with jQuery 3.3.1 on a Windows 10 machine running Node 8.9.4 and Express 4.16.2 我正在运行Node 8.9.4和Express 4.16.2的Windows 10计算机上使用带有jQuery 3.3.1的Google Chrome 63

My work-around is to chain the ajax requests so that each request doesn't get called until the prior request has received a response from the server. 我的解决方法是将ajax请求链接起来,以便在先前的请求收到服务器的响应之前,不调用每个请求。 But I shouldn't have to do that... 但我不必这样做...

Here is the relevant server code: 这是相关的服务器代码:

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;

    }
};

You cannot store req and res in global or module-level variables. 您不能将reqres存储在全局或模块级变量中。 When a second request comes in, it will immediately overwrite your globals and it will mix up the data for the various requests. 当出现第二个请求时,它将立即覆盖您的全局变量,并将混合各种请求的数据。

Does't node separate each request instance? 节点不分隔每个请求实例吗?

Yes, there is a separate request instance, but not a separate global or module-level namespace. 是的,有一个单独的请求实例,但没有一个单独的全局或模块级名称空间。 So, when you assign the req into the global space, you are overwriting the previous one and your code will then use the wrong one. 因此,当您将req分配到全局空间时,您将覆盖前一个,然后您的代码将使用错误的代码。

It is very helpful to have the request and response as a global variable. 将请求和响应作为全局变量非常有帮助。 Otherwise I would have to be passing them all over the place. 否则,我将不得不在整个地方传递它们。

You HAVE to pass them to lower level functions that need them. 您必须将它们传递给需要它们的较低级别的函数。 That's how you keep each request separate from the others. 这就是您将每个请求与其他请求分开的方式。 ANY function that needs to operate on req or res should be passed those variables so it knows exactly which ones to operate on. 任何需要对reqres进行操作的函数都应传递这些变量,以便它确切知道要对哪个进行操作。

node.js has a shared global and module-level namespace. node.js具有共享的全局和模块级名称空间。 So, all requests in flight at the same time use that same namespace. 因此,所有同时进行的请求都使用相同的名称空间。 The ONLY data that should ever be stored there is data that you specifically want to be shared between requests (such as session state, for example). 仅应存储的数据是您特别希望在请求之间共享的数据(例如,会话状态)。 Individual request or response objects should never be stored in a shared variable. 单独的请求或响应对象不应存储在共享变量中。


A more common way to code your type of code is to call a function like your GetQueryData() and have it return the data (likely via a promise) and then you send the response in the original request handler. 编码您的代码类型的更常见方法是调用GetQueryData()类的函数,并使其返回数据(可能通过GetQueryData() ),然后在原始请求处理程序中发送响应。 Then, you don't have to pass req or res down multiple levels at all. 然后,您根本不必通过reqres向下传递多个级别。 Your helper functions just fetch data. 您的助手功能只是获取数据。 The request handlers fetch data, then send the response. 请求处理程序获取数据,然后发送响应。 It's often a better encapsulation of functionality. 通常,这是对功能的更好封装。


Here's one way to restructure your code along the lines described above. 这是按照上述方式重组代码的一种方法。

  1. GetQueryData() returns a promise that fulfills with the data GetQueryData()返回一个满足数据的承诺
  2. ParseMySqlRowsData() just returns a parsed result or null if a parsing error ParseMySqlRowsData()仅返回解析结果,如果解析错误,则返回null
  3. app.post() just gets the data (via a promise) and then sends the appropriate response. app.post()只是通过诺言获取数据,然后发送适当的响应。
  4. There's no global req or res and there's no need to pass them anywhere. 有没有全球性的reqres ,有没有必要在任何地方通过他们。

Here's the code: 这是代码:

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 I see you still have a module-level variable you should not have: currentDataRowParser . PS我看到你仍然有一个模块级别的变量,你不应该有: currentDataRowParser That needs to be replaced by passing the appropriate parser to ParseMySqlRowsData() as an argument, not using a module-level shared variable. 需要通过将适当的解析器作为参数传递给ParseMySqlRowsData()ParseMySqlRowsData() ,而不使用模块级共享变量。 I will leave that as an excercise for you to fix. 我将把它作为练习内容供您解决。 Shared variables for operating on a specific request state are a bad idea in ANY server programming and specifically in node.js programming. 在任何服务器编程中,特别是在node.js编程中,用于对特定请求状态进行操作的共享变量都是一个坏主意。 Keep your request-specific state in your function arguments or on the req or res object itself. 将特定于请求的状态保留在函数参数中或reqres对象本身上。 That's how you prevent overwritting the data from one request with the data from another while they are being processed. 这样一来,您就可以防止在处理一个请求时将一个请求的数据覆盖另一个请求的数据。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM