Javascript Nodejs确保API登录只发生一次
我为Node.js编写了以下代码,以连接到CMS的API。API必须使用用户名和密码登录一次,然后使用访问令牌继续执行请求。但是,访问令牌的有效时间有限-之后API必须再次登录并接收新令牌 当我向API发出请求时,我总是尝试使用当前令牌-如果它不再有效,我将收到403并触发新登录,然后再次执行初始请求 这里的问题是,如果多个请求同时尝试访问API,它们都会触发登录-这会导致CMS中出现多个API会话,这是一件坏事。我以为我已经用承诺处理了那个案子,但显然我的代码并不像我想象的那样工作Javascript Nodejs确保API登录只发生一次,javascript,node.js,asynchronous,promise,request,Javascript,Node.js,Asynchronous,Promise,Request,我为Node.js编写了以下代码,以连接到CMS的API。API必须使用用户名和密码登录一次,然后使用访问令牌继续执行请求。但是,访问令牌的有效时间有限-之后API必须再次登录并接收新令牌 当我向API发出请求时,我总是尝试使用当前令牌-如果它不再有效,我将收到403并触发新登录,然后再次执行初始请求 这里的问题是,如果多个请求同时尝试访问API,它们都会触发登录-这会导致CMS中出现多个API会话,这是一件坏事。我以为我已经用承诺处理了那个案子,但显然我的代码并不像我想象的那样工作 var a
var api = {
url: 'servername',
user: 'user',
password: 'password',
token: null
};
var login_in_progress = true;
var loginPromise;
function loginApi() {
login_in_progress = true;
return new Promise(function (resolve, reject) {
request({
uri: api.url + api.user + ',' + api.password,
method: 'GET',
json: true
}, function (err, res, data) {
if (!err && res.statusCode === 200) {
api.token = data.token;
login_in_progress = false;
resolve(data);
} else {
login_in_progress = false;
reject(err);
}
});
});
}
var getContent = function (query) {
// Currently a login is running - wait till it's finished and then execute get
// at least that was the idea - but does not seem to work
if (login_in_progress && loginPromise) {
return new Promise(function (resolve, reject) {
loginPromise
.then(function () {
getContent(query)
.then(function (data) {
resolve(data);
})
.catch(function (err) {
reject(err);
});
})
.catch(function (err) {
reject(err);
});
});
} else {
// Do the actual request
// case 403 Api is logged out => reLogin
loginPromise = loginApi();
loginPromise
.then(function () {
getContent(query)
.then(function (data) {
resolve(data);
})
.catch(function (err) {
reject(err);
});
})
.catch(function (err) {
reject(err)
});
}
}
显然,检查登录当前是否未运行是不可能的,并且getContent函数总是在else语句中运行。我甚至不确定我检查正在运行的登录请求的想法是否会以这种方式起作用。我相信类似的方法可以起作用(注意:这实际上是“伪代码”,而不是可运行的代码):
唯一的状态是是否有承诺。我相信类似的东西可以工作(注意:这实际上是“伪代码”,而不是可运行的代码):
唯一的状态是是否存在承诺。在代码中有一些事情可以纠正或简化。我在这里用变量
testlogged
模拟了登录和注销,该变量替换了原始代码中的403响应,并用超时模拟了登录延迟,只是为了演示如何用最少的代码完成
注:
- 您似乎不需要在
中嵌套这些承诺。这个函数是否使用另一个函数来获取返回承诺的内容,或者是否需要返回承诺本身,目前还不清楚。这不是一回事。对于第二种情况,您可以参见示例2getContent
- 您需要对
返回的内容保持一致,并了解您希望成为承诺的内容。要么将回调传递给getContent
(示例1),要么对其使用getContent
,然后使用
(示例2)
- 在示例1中,在自身内部调用
非常简单,但如果它返回一个承诺,我不希望返回,因为它带来了额外的复杂性,强制在函数本身内部对其使用getContent
- 例如,如果
从一开始就返回一个新的承诺,这将更加清楚,它将封装所有代码getContent
var-api={
url:'服务器名',
用户:“用户”,
密码:“password”,
令牌:空
};
var testlogged=false;
var login_in_progress=true;
var-loginPromise;
函数loginApi(){
login_in_progress=true;
返回新承诺(功能(解决、拒绝){
setTimeout(函数(){
log('login OK');
登录\u in\u progress=false;
testlogged=true;
解决();
}, 10);
});
}
函数getContent(查询、回调){
//当前正在运行登录-等待登录完成,然后执行get
如果(正在登录和登录建议){
log('正在登录,在获取内容之前等待..query:'+query);
登录建议
.然后(函数(){
回调('获取内容确定(1)查询:'+查询);
})
.catch(函数(err){
console.error('error:',err);
});
}否则{
//按实际要求做
如果(!testlogged){
//案例403 Api已注销=>重新登录
log('starting login..query:'+query);
loginPromise=loginApi();
//重新启动函数以将查询放入队列,保存代码,但您也可以直接登录Promise。。。
getContent(查询、回调);
}否则{
console.log(“已记录”);
回调('获取内容确定(2)查询:'+查询);
}
}
}
var theCallback=函数(数据){
控制台日志(数据);
};
//在未记录的情况下进行首次尝试
获取内容(“测试”,回调);
//在仍记录日志的情况下尝试并发尝试
获取内容(“测试2”,回调);
//在仍然记录的情况下模拟尝试,然后在注销后模拟尝试(首先等待前两次尝试完成)
setTimeout(函数(){
获取内容(“测试3”,回调);
}, 100);
setTimeout(函数(){
console.log(“模拟注销”);
testlogged=false;
获取内容(“测试4”,回调);
},100);
在您的代码中有一些事情可以更正或简化。我在这里使用变量testlogged
模拟登录和注销,该变量替换原始代码中的403响应,并使用超时来模拟登录延迟,只是为了演示如何用最少的代码完成
注:
- 您似乎不需要在
中有这些嵌套的承诺。不清楚此函数是否使用另一个函数来获取返回承诺的内容,或者是否需要返回承诺本身。这与此不同。对于第二种情况,您可以看到示例2getContent
- 您需要对
返回的内容保持一致,了解您希望成为承诺的内容。您可以将回调传递给getContent
(示例1),或者使用getContent
然后对其进行
(示例2)
- 在示例1中,在自身内部调用
非常简单,但如果它返回一个承诺,我不希望返回,因为它带来了额外的复杂性,强制在函数本身内部对其使用getContent
- 例如2,如果
从一开始就返回一个新的承诺,则更为清晰,getContent
let authPromise = undefined; function getContent(query) { if (!authPromise) { // no login pending - start one, then restart the whole function authPromise = login(); return authPromise.then(() => getContent(query)); } else { // login pending - wait for it, then make the real call // if the login promise already resolved, it won't wait return authPromise.then(() => makeAjaxRequest(query)).then((response) => { if (response.status === 403) { // session expired - remove the state, and restart the whole function authPromise = undefined; return getContent(query); } else { // session still valid, return the API response return response; } }); } }
// case 403 Api is logged out => reLogin // check again if login is running NOW, only if not, start one if(!login_in_progress){ loginPromise = loginApi(); } // Always attach to the loginPromise to redo the initial getContent loginPromise .then(function () { getContent(query) .then(function (data) { resolve(data); }) .catch(function (err) { reject(err); }); }) .catch(function (err) { reject(err) });