我正在计算机上开发NodeJS / ExpressJS应用程序。我有节点在本地运行。我有一个单页网页应用程序。当它需要信息时,它使用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;
}
};
答案 0 :(得分:3)
您无法在全局或模块级变量中存储req
和res
。当第二个请求进入时,它会立即覆盖你的全局变量,它会混淆各种请求的数据。
是否有节点将每个请求实例分开?
是的,有一个单独的请求实例,但不是单独的全局或模块级命名空间。因此,当您将req
分配到全局空间时,您将覆盖前一个空格,然后您的代码将使用错误的代码。
将请求和响应作为全局变量非常有用。否则我将不得不将它们传遍整个地方。
您必须将它们传递给需要它们的低级功能。这就是你如何将每个请求与其他请求分开。任何需要在req
或res
上运行的函数都应该传递给那些变量,以便它确切地知道要操作哪些变量。
node.js具有共享的全局和模块级命名空间。因此,所有在飞行中的请求同时使用相同的命名空间。应该存储的唯一数据是您特别希望在请求之间共享的数据(例如,会话状态)。不应将单个请求或响应对象存储在共享变量中。
编写代码类型的一种更常见的方法是调用类似GetQueryData()
的函数并让它返回数据(可能通过promise),然后在原始请求处理程序中发送响应。然后,您根本不必将req
或res
传递到多个级别。你的帮助函数只是获取数据。请求处理程序获取数据,然后发送响应。它通常是更好的功能封装。
以下是按照上述方法重新构建代码的一种方法。
GetQueryData()
返回履行数据的承诺ParseMySqlRowsData()
只返回解析后的结果,如果解析错误null
app.post()
只获取数据(通过承诺),然后发送相应的响应。req
或res
,并且无需将它们传递到任何地方。以下是代码:
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);
});
};
P.S。我看到你还有一个你不应该拥有的模块级变量:currentDataRowParser
。需要通过将适当的解析器作为参数传递给ParseMySqlRowsData()
来替换,而不是使用模块级共享变量。我将把它作为锻炼的一部分来解决。在特定请求状态下操作的共享变量在任何服务器编程中都是一个坏主意,特别是在node.js编程中。将您的特定于请求的状态保存在函数参数或req
或res
对象本身中。这就是如何防止在处理过程中使用来自另一个请求的数据覆盖来自一个请求的数据。