Hi there I'm doing a very simple API using NodeJS, ExpressJS and MySQLJs. I have a little form that make the request login
request to the server. The problem I have is when I'm trying to do the same request twice after the first has been finished, the server answer me with a ERR_CONNECTION_REFUSED
I was using this tutorial in order to make my code a little bit cleaner. I've changed a little because I think class makes the code cleaner than using functions
.
https://code.tutsplus.com/tutorials/managing-the-asynchronous-nature-of-nodejs--net-36183
This is the full backtrace of the error I have
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)
This is my main javascript where I define the main structure of my simple application. In the log I can see that the second request is enter in success callback, what am I missing?
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 );
});
And this is the login method of the User 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;
}
});
}
I'm using this Javascript in front side to get all the values from the login form and send it to the 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 );
})();
Thanks in advance.
答案 0 :(得分:2)
You are registering event handlers for every request. The problem is, you're not removing any of the request handlers afterwards.
For example, if you did 10 POST requests to the login endpoint, and then you do the 11th. If the 11th's is successful, the success
event is triggered for the current request but also all the other 10.
The reason you're getting Cannot set headers after they are sent to the client
is because the event listener is being triggered for a HTTP request that already completed.
The solution is to architect this different. The EventListener is not a good model for calling functions and getting results. EventListener is good for things that can trigger many times. In this case you don't want that, you only want 1 success or 1 failure per request.
The right model for this is:
A) A simple function call. It can return information related to it's success and throw an exception if it failed.
B) Return promises. Resolve them if login was successful, Reject them if not.
C) Use async/await. Again: return the result
if it was successful, throw an error if not.
D) Use the callback pattern that you're already using with express. So pass a callback with a err
and result
parameter.
The last option might be preferable because you're working with a framework that doesn't support Promises and async/await well.
答案 1 :(得分:1)
Create User object inside app.post
api call instead of creating it globally.