Javascript Node.js同时请求自身失败的REST API
我使用node.js创建了一个REST API服务,它工作得非常好,直到我在浏览器中打开了几个选项卡,并且(几乎)同时发出了几个请求。发送请求的最后一个选项卡获得响应,另一个选项卡挂起 基本上,我有一个名为“服务”的模块,它完成所有工作:Javascript Node.js同时请求自身失败的REST API,javascript,json,node.js,rest,Javascript,Json,Node.js,Rest,我使用node.js创建了一个REST API服务,它工作得非常好,直到我在浏览器中打开了几个选项卡,并且(几乎)同时发出了几个请求。发送请求的最后一个选项卡获得响应,另一个选项卡挂起 基本上,我有一个名为“服务”的模块,它完成所有工作: var service = require('./service'); var server = http.createServer(function(req, res) { ... var serviceResult = service.ru
var service = require('./service');
var server = http.createServer(function(req, res) {
...
var serviceResult = service.runService(parsedURL.query.username, parsedURL.query.api_key);
res.writeHead(200, {'content-type': 'application/json', 'connection' : 'keep-alive' });
service.ready = function(serviceResult) {
var serviceResultJSON = JSON.stringify(serviceResult);
res.writeHead(200, {'content-type': 'application/json', 'connection' : 'close' });
res.end(serviceResultJSON);
}
}
从服务模块,我呼叫:
service.ready(result);
。。。只要结果准备好了,简而言之,这就是我的服务器。那么我能做些什么来解决这个问题呢
编辑:
下面是my service.js模块的外观(在建议的更改之后):
在这种情况下,模块会像“server.js”文件中的“service”模块一样被覆盖,对吗?
我猜我需要在任何地方实现回调,而不是.ready()函数,对吗?问题是它们都共享同一个服务对象。所以,当您收到一个请求时,您会覆盖service.ready以响应该请求的函数。因此,如果您收到两个非常接近的请求,service.ready将设置为响应您收到的最后一个请求,因此,只有该请求才会得到响应 最好的方法是让服务模块导出一个函数,该函数返回如下服务实例:
function serviceFactory() {
}
serviceFactory.prototype.getService() {
return new Service();
}
module.exports = serviceFactory;
然后你就可以
var serviceFactory = require(./service);
var server = http.createServer(function(req, res) {
...
var service = serviceFactory.getService();
var serviceResult = service.runService(parsedURL.query.username, parsedURL.query.api_key);
res.writeHead(200, {'content-type': 'application/json', 'connection' : 'keep-alive' });
service.ready = function(serviceResult) {
var serviceResultJSON = JSON.stringify(serviceResult);
res.writeHead(200, {'content-type': 'application/json', 'connection' : 'close' });
res.end(serviceResultJSON);
}
}
问题是它们都共享同一个服务对象。所以,当您收到一个请求时,您会覆盖service.ready以响应该请求的函数。因此,如果您收到两个非常接近的请求,service.ready将设置为响应您收到的最后一个请求,因此,只有该请求才会得到响应 最好的方法是让服务模块导出一个函数,该函数返回如下服务实例:
function serviceFactory() {
}
serviceFactory.prototype.getService() {
return new Service();
}
module.exports = serviceFactory;
然后你就可以
var serviceFactory = require(./service);
var server = http.createServer(function(req, res) {
...
var service = serviceFactory.getService();
var serviceResult = service.runService(parsedURL.query.username, parsedURL.query.api_key);
res.writeHead(200, {'content-type': 'application/json', 'connection' : 'keep-alive' });
service.ready = function(serviceResult) {
var serviceResultJSON = JSON.stringify(serviceResult);
res.writeHead(200, {'content-type': 'application/json', 'connection' : 'close' });
res.end(serviceResultJSON);
}
}
这里有三个问题
服务
上设置就绪
属性,而只有一个服务
对象。如果两个请求发生得非常近,那么在第一个请求被触发之前,您可能会用第二个请求覆盖ready
属性。这是因为服务
对象只有一个实例。这几乎肯定是应该的方式,所以不用担心,但是我们必须找到一种新的方式来向您的消费代码发出服务操作已经完成的信号服务中的res
变量。read
可能与您认为的res
不同。第二次打电话,情况就不一样了。这是因为您正在重新定义ready
属性,因此每次都会得到不同的作用域var service = require('./service');
var server = http.createServer(function(req, res) {
// note that we're not immediately sending headers anymore - this
// fixes the third problem with what you had before
service.runService(parsedURL.query.username, parsedURL.query.api_key, function(err, serviceResult) {
// check for errors! tell the client!
if (err) {
res.writeHead(500);
res.end();
return;
}
res.writeHead(200, {
'content-type': 'application/json',
'connection' : 'keep-alive',
});
var serviceResultJSON = JSON.stringify(serviceResult);
res.end(serviceResultJSON);
});
};
下面是我如何实现服务的方法:
var service = module.exports = {};
service.runService = function runService(username, key, cb) {
// assume `database` exists from somewhere else...
database.getUser(username, function(err, user) {
// make sure we send any errors up the line
if (err) {
cb(err);
return;
}
// here's an error we've decided on
if (user.key !== key) {
cb(Error("key is incorrect!"));
return;
}
// this is a very common usage pattern - cb(error, result, ...)
// the reason we're calling this will `null` for the error is a bit
// of a double negative - we're saying "there was no error".
cb(null, user);
});
};
这里有三个问题
您正在服务
上设置就绪
属性,而只有一个服务
对象。如果两个请求发生得非常近,那么在第一个请求被触发之前,您可能会用第二个请求覆盖ready
属性。这是因为服务
对象只有一个实例。这几乎肯定是应该的方式,所以不用担心,但是我们必须找到一种新的方式来向您的消费代码发出服务操作已经完成的信号
服务中的res
变量。read
可能与您认为的res
不同。第二次打电话,情况就不一样了。这是因为您正在重新定义ready
属性,因此每次都会得到不同的作用域
您将发送两次标题。这是一个简单的解决办法-只是不要发送任何标题,直到你知道他们应该是什么。这将在下面变得显而易见
要解决第一个问题,我建议您使用回调!您应该已经从节点核心熟悉了它们,它们几乎用于所有方面。这也恰好解决了第二个问题,借助于JavaScript中的
注意:我建议使用回调而不是工厂或其他方法的原因是回调在节点领域非常普遍,因此如果使用回调,很有可能会更容易与其他库等集成
以下是我如何编写您的服务器代码:
var service = require('./service');
var server = http.createServer(function(req, res) {
// note that we're not immediately sending headers anymore - this
// fixes the third problem with what you had before
service.runService(parsedURL.query.username, parsedURL.query.api_key, function(err, serviceResult) {
// check for errors! tell the client!
if (err) {
res.writeHead(500);
res.end();
return;
}
res.writeHead(200, {
'content-type': 'application/json',
'connection' : 'keep-alive',
});
var serviceResultJSON = JSON.stringify(serviceResult);
res.end(serviceResultJSON);
});
};
下面是我如何实现服务的方法:
var service = module.exports = {};
service.runService = function runService(username, key, cb) {
// assume `database` exists from somewhere else...
database.getUser(username, function(err, user) {
// make sure we send any errors up the line
if (err) {
cb(err);
return;
}
// here's an error we've decided on
if (user.key !== key) {
cb(Error("key is incorrect!"));
return;
}
// this is a very common usage pattern - cb(error, result, ...)
// the reason we're calling this will `null` for the error is a bit
// of a double negative - we're saying "there was no error".
cb(null, user);
});
};
由于对require
的调用是缓存的,所以您的快速修复实际上不会起作用。每次调用require('./service')
,都会得到相同的对象。这是详细的。没错,我忘了。编辑了回复!谢谢工厂方法模式在node.js中不是很常见(或有用);特别是在异步方面。详见@deoxa的答案。@naomik我知道这一点。不太常见的事实并不意味着它没有用处,也不意味着它应该被忽略。我同意Deoxa答案中的回调解决方案更理想,更符合典型的节点代码流。我只是尽量保留作者的代码结构,以便他能看到更多的内容