【问题标题】:Error with HTTP header when submit twice the same request提交两次相同的请求时 HTTP 标头出错
【发布时间】:2018-09-18 20:08:27
【问题描述】:

您好,我正在使用 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 );
});

这是User类的登录方法

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。

ma​​in.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 );

})();

提前致谢。

【问题讨论】:

  • 使用send的方式相同。

标签: node.js express mysqljs


【解决方案1】:

您正在为每个请求注册事件处理程序。问题是,您之后不会删除任何请求处理程序。

例如,如果您向登录端点发出了 10 个 POST 请求,然后您执行了第 11 个。如果第 11 次成功,则为当前请求触发 success 事件,其他 10 次也将触发。

您收到Cannot set headers after they are sent to the client 的原因是因为正在为已完成的 HTTP 请求触发事件侦听器。

解决方案是构建这种不同的架构。 EventListener 不是调用函数和获取结果的好模型。 EventListener 适用于可以多次触发的事情。在这种情况下,您不希望这样,您只希望每个请求 1 次成功或 1 次失败。

正确的模型是:

A) 一个简单的函数调用。它可以返回与其成功相关的信息,如果失败则抛出异常。

B) 回报承诺。如果登录成功则解决它们,如果不成功则拒绝它们。

C) 使用异步/等待。同样:如果成功则返回result,否则抛出错误。

D) 使用您已经在 express 中使用的回调模式。所以使用errresult 参数传递一个回调。

最后一个选项可能更可取,因为您使用的框架不能很好地支持 Promises 和 async/await。

【讨论】:

  • 你能举一个D选项的小例子吗?我是 NodeJS 的新手,所以我不知道如何正确地做到这一点。非常感谢!
  • 我明白问题是什么,但我不知道如何解决。
【解决方案2】:

app.post api 调用中创建用户对象,而不是全局创建它。

【讨论】:

  • 我还是有同样的问题。
  • 你能附上你正在尝试这个的示例代码吗?需要看完整代码。
  • 我用我用于 POST 请求的主要 javascript 更新了问题。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-07-13
  • 1970-01-01
  • 1970-01-01
  • 2021-03-30
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多