提交两次相同请求时出现 HTTP header 错误
Error with HTTP header when submit twice the same request
您好,我正在使用 NodeJS、ExpressJS 和 MySQLJs 做一个非常简单的 API。我有一个小表格可以向服务器发出请求 login
请求。我遇到的问题是,当我在第一个请求完成后尝试两次执行相同的请求时,服务器用 ERR_CONNECTION_REFUSED
回答我
我使用本教程是为了使我的代码更简洁一些。我做了一些改动,因为我认为 class 比使用 functions
.
使代码更简洁
https://code.tutsplus.com/tutorials/managing-the-asynchronous-nature-of-nodejs--net-36183
这是我遇到的错误的完整回溯
Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
at ServerResponse.setHeader (_http_outgoing.js:470:11)
at User.<anonymous> (/home/ismael/Projects/internal_project/app.js:84:9)
at User.emit (events.js:187:15)
at Query.<anonymous> (/home/ismael/Projects/internal_project/model/user.js:20:14)
at Query.<anonymous> (/home/ismael/Projects/internal_project/node_modules/mysql/lib/Connection.js:502:10)
at Query._callback (/home/ismael/Projects/internal_project/node_modules/mysql/lib/Connection.js:468:16)
at Query.Sequence.end (/home/ismael/Projects/internal_project/node_modules/mysql/lib/protocol/sequences/Sequence.js:83:24)
at Query._handleFinalResultPacket (/home/ismael/Projects/internal_project/node_modules/mysql/lib/protocol/sequences/Query.js:139:8)
at Query.EofPacket (/home/ismael/Projects/internal_project/node_modules/mysql/lib/protocol/sequences/Query.js:123:8)
at Protocol._parsePacket (/home/ismael/Projects/internal_project/node_modules/mysql/lib/protocol/Protocol.js:278:23)
这是我的主要 javascript,我在其中定义我的简单应用程序的主要结构。在日志中我可以看到第二个请求进入成功回调,我错过了什么?
app.post( '/login/', function(req, res){
user.on('success', function(result){
logger.debug( '[SUCCESS]' );
res.cookie('user', { name : result.name, id : result.id });
res.setHeader( 'Content-Type', 'application/json' );
res.status(200).end( JSON.stringify({success : true}) );
});
user.on('failure', function(reason){
logger.debug( '[FAILURE]' );
res.setHeader( 'Content-Type', 'application/json' );
res.status(400).send( JSON.stringify({success: false}) );
});
user.on('error', function(error){
// logger.debug( '[ERROR]' );
//
// res.setHeader( 'Content-Type', 'application/json' );
// res.status(500).send( JSON.stringify({ error: 'Internal error' }) );
});
user.login( req.body.username, req.body.password );
});
这是用户的登录方法class
login ( username, password ){
var that = this;
this.sql.query( "SELECT * FROM user WHERE name = ? AND password = ?", [ username, password ], function(error, result, fields){
// if ( error ){
// that.emit('error', error);
// return true;
// }
if ( result.length == 0 ){
that.emit( 'failure' );
return true;
}
if ( result.length >= 1 ){
that.emit( 'success', result[0] );
return true;
}
});
}
我在前端使用这个 Javascript 从登录表单中获取所有值并将其发送到 API。
main.js
(function(){
var _loginForm = {},
_loginUsername = {},
_loginPassword = {},
_loginConfig = {
url : '/login/',
method : 'POST'
},
_loginRequest = new XMLHttpRequest();
var
_onLoginRequest = function(){
console.log( this );
if ( this.readyState == 4 ){
JSON.parse(this.responseText)
}
},
_onLoginSubmit = function(evt){
evt.preventDefault();
_loginRequest.onreadystatechange = _onLoginRequest;
_loginRequest.open( _loginConfig.method, _loginConfig.url, true );
_loginRequest.setRequestHeader( 'Content-Type', 'application/json' );
_loginRequest.send(JSON.stringify({
username : _loginUsername.value,
password : _loginPassword. value
}));
},
_onDomLoad = function(evt){
_loginForm = document.getElementById( 'login-form' );
_loginUsername = document.getElementById( 'username' );
_loginPassword = document.getElementById( 'password' );
_loginForm.addEventListener( 'submit', _onLoginSubmit );
};
document.addEventListener( 'DOMContentLoaded', _onDomLoad );
})();
提前致谢。
您正在为每个请求注册事件处理程序。问题是,您之后没有删除任何请求处理程序。
例如,如果您向登录端点发出了 10 次 POST 请求,然后您执行了第 11 次。如果第 11 个成功,则会为当前请求触发 success
事件,但也会为所有其他 10 个请求触发事件。
您收到 Cannot set headers after they are sent to the client
的原因是事件侦听器正在为已完成的 HTTP 请求触发。
解决方案是构建不同的架构。 EventListener 不是调用函数和获取结果的好模型。 EventListener 适用于可以多次触发的事物。在这种情况下,您不希望这样,每个请求只需要 1 次成功或 1 次失败。
正确的模型是:
A) 一个简单的函数调用。它可以 return 与成功相关的信息,如果失败则抛出异常。
B) Return 承诺。登录成功则解决,登录不成功则拒绝
C) 使用 async/await。再次:return result
如果成功,则抛出错误。
D) 使用您已经在 express 中使用的回调模式。因此,传递带有 err
和 result
参数的回调。
最后一个选项可能更可取,因为您正在使用不支持 Promises 并且 async/await 很好的框架。
在 app.post
api 调用中创建用户对象,而不是全局创建它。
您好,我正在使用 NodeJS、ExpressJS 和 MySQLJs 做一个非常简单的 API。我有一个小表格可以向服务器发出请求 login
请求。我遇到的问题是,当我在第一个请求完成后尝试两次执行相同的请求时,服务器用 ERR_CONNECTION_REFUSED
我使用本教程是为了使我的代码更简洁一些。我做了一些改动,因为我认为 class 比使用 functions
.
https://code.tutsplus.com/tutorials/managing-the-asynchronous-nature-of-nodejs--net-36183
这是我遇到的错误的完整回溯
Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
at ServerResponse.setHeader (_http_outgoing.js:470:11)
at User.<anonymous> (/home/ismael/Projects/internal_project/app.js:84:9)
at User.emit (events.js:187:15)
at Query.<anonymous> (/home/ismael/Projects/internal_project/model/user.js:20:14)
at Query.<anonymous> (/home/ismael/Projects/internal_project/node_modules/mysql/lib/Connection.js:502:10)
at Query._callback (/home/ismael/Projects/internal_project/node_modules/mysql/lib/Connection.js:468:16)
at Query.Sequence.end (/home/ismael/Projects/internal_project/node_modules/mysql/lib/protocol/sequences/Sequence.js:83:24)
at Query._handleFinalResultPacket (/home/ismael/Projects/internal_project/node_modules/mysql/lib/protocol/sequences/Query.js:139:8)
at Query.EofPacket (/home/ismael/Projects/internal_project/node_modules/mysql/lib/protocol/sequences/Query.js:123:8)
at Protocol._parsePacket (/home/ismael/Projects/internal_project/node_modules/mysql/lib/protocol/Protocol.js:278:23)
这是我的主要 javascript,我在其中定义我的简单应用程序的主要结构。在日志中我可以看到第二个请求进入成功回调,我错过了什么?
app.post( '/login/', function(req, res){
user.on('success', function(result){
logger.debug( '[SUCCESS]' );
res.cookie('user', { name : result.name, id : result.id });
res.setHeader( 'Content-Type', 'application/json' );
res.status(200).end( JSON.stringify({success : true}) );
});
user.on('failure', function(reason){
logger.debug( '[FAILURE]' );
res.setHeader( 'Content-Type', 'application/json' );
res.status(400).send( JSON.stringify({success: false}) );
});
user.on('error', function(error){
// logger.debug( '[ERROR]' );
//
// res.setHeader( 'Content-Type', 'application/json' );
// res.status(500).send( JSON.stringify({ error: 'Internal error' }) );
});
user.login( req.body.username, req.body.password );
});
这是用户的登录方法class
login ( username, password ){
var that = this;
this.sql.query( "SELECT * FROM user WHERE name = ? AND password = ?", [ username, password ], function(error, result, fields){
// if ( error ){
// that.emit('error', error);
// return true;
// }
if ( result.length == 0 ){
that.emit( 'failure' );
return true;
}
if ( result.length >= 1 ){
that.emit( 'success', result[0] );
return true;
}
});
}
我在前端使用这个 Javascript 从登录表单中获取所有值并将其发送到 API。
main.js
(function(){
var _loginForm = {},
_loginUsername = {},
_loginPassword = {},
_loginConfig = {
url : '/login/',
method : 'POST'
},
_loginRequest = new XMLHttpRequest();
var
_onLoginRequest = function(){
console.log( this );
if ( this.readyState == 4 ){
JSON.parse(this.responseText)
}
},
_onLoginSubmit = function(evt){
evt.preventDefault();
_loginRequest.onreadystatechange = _onLoginRequest;
_loginRequest.open( _loginConfig.method, _loginConfig.url, true );
_loginRequest.setRequestHeader( 'Content-Type', 'application/json' );
_loginRequest.send(JSON.stringify({
username : _loginUsername.value,
password : _loginPassword. value
}));
},
_onDomLoad = function(evt){
_loginForm = document.getElementById( 'login-form' );
_loginUsername = document.getElementById( 'username' );
_loginPassword = document.getElementById( 'password' );
_loginForm.addEventListener( 'submit', _onLoginSubmit );
};
document.addEventListener( 'DOMContentLoaded', _onDomLoad );
})();
提前致谢。
您正在为每个请求注册事件处理程序。问题是,您之后没有删除任何请求处理程序。
例如,如果您向登录端点发出了 10 次 POST 请求,然后您执行了第 11 次。如果第 11 个成功,则会为当前请求触发 success
事件,但也会为所有其他 10 个请求触发事件。
您收到 Cannot set headers after they are sent to the client
的原因是事件侦听器正在为已完成的 HTTP 请求触发。
解决方案是构建不同的架构。 EventListener 不是调用函数和获取结果的好模型。 EventListener 适用于可以多次触发的事物。在这种情况下,您不希望这样,每个请求只需要 1 次成功或 1 次失败。
正确的模型是:
A) 一个简单的函数调用。它可以 return 与成功相关的信息,如果失败则抛出异常。
B) Return 承诺。登录成功则解决,登录不成功则拒绝
C) 使用 async/await。再次:return result
如果成功,则抛出错误。
D) 使用您已经在 express 中使用的回调模式。因此,传递带有 err
和 result
参数的回调。
最后一个选项可能更可取,因为您正在使用不支持 Promises 并且 async/await 很好的框架。
在 app.post
api 调用中创建用户对象,而不是全局创建它。