Ошибка [ERR_HTTP_HEADERS_SENT]: не удается установить заголовки после их отправки клиенту, при втором нажатии кнопки

#node.js #express #flutter #dart

#node.js #экспресс #трепетание #dart

Вопрос:

В настоящее время я изучаю Flutter / DART и пытаюсь создать приложение, которое взаимодействует с сервером, но у меня проблемы. Есть кнопка входа, которая отправляет запрос на сервер, сервер проверяет, есть ли пользователь с этим именем пользователя и паролем, и ответ отправляется обратно. При первом нажатии кнопки все работает нормально, но при повторном нажатии кнопки и т. Д. На сервере появляются всплывающие окна с ошибкой:

 Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
    at ServerResponse.setHeader (_http_outgoing.js:526:11)
    at ServerResponse.header (C:UsersPCDesktopFlutter Node.js Login-Register Appservernode_modulesexpresslibresponse.js:771:10)
    at ServerResponse.send (C:UsersPCDesktopFlutter Node.js Login-Register Appservernode_modulesexpresslibresponse.js:170:12)
    at ServerResponse.json (C:UsersPCDesktopFlutter Node.js Login-Register Appservernode_modulesexpresslibresponse.js:267:15)
    at ServerResponse.send (C:UsersPCDesktopFlutter Node.js Login-Register Appservernode_modulesexpresslibresponse.js:158:21)
    at Function.<anonymous> (C:UsersPCDesktopFlutter Node.js Login-Register Appserverserver.js:14:39)
    at Function.emit (events.js:326:22)
    at Query.<anonymous> (C:UsersPCDesktopFlutter Node.js Login-Register Appserverdb.js:17:20)
    at Query.<anonymous> (C:UsersPCDesktopFlutter Node.js Login-Register Appservernode_modulesmysqllibConnection.js:526:10)
    at Query._callback (C:UsersPCDesktopFlutter Node.js Login-Register Appservernode_modulesmysqllibConnection.js:488:16) {
  code: 'ERR_HTTP_HEADERS_SENT'
}
  

Вот код из приложения flutter и из node.js сервер:

Flutter: функция вызывается при нажатии кнопки:

 void signIn(username, password) async {
    final bodyEncoded = jsonEncode(
      {
        'username': model.username,
        'password': model.password,
      },
    );

    final response = await http.post(
      'http://192.168.0.110:3000/signin',
      headers: {'Content-Type': 'application/json'},
      body: bodyEncoded,
    );

    if (response.body == 'true')
      Navigator.push(
        context,
        MaterialPageRoute(
          builder: (context) => MainBody(),
        ),
      );
    else
      setState(() => {elementOpacity = 1.0});
  }
  

Сервер

server.js

 const express = require('express');
const app = express();
const server = require('./server.json');

exports.app = app;
const db = require('./db');

app.use(express.json());

app.post('/signin', (req, res) => {
    const {username, password} = req.body;
    app.emit('signInRequest', username, password);

    app.on('response', (value) => res.send(value));
});

app.listen(server.port, () => console.log('Server running on port '   server.port));
  

db.js

 const mysql = require('mysql');
const connectionUri = require('./db.json');
const db = mysql.createConnection(connectionUri);
const app = require('./server').app;

db.connect((err) => {
    if (err)
        throw err;
    console.log('MySQL connection successful!');
});

app.on('signInRequest', (username, password) => {
    db.query("SELECT Count(*) AS 'count' FROM Users WHERE (Username = '"   username   "' AND Password = '"   password   "')", (err, result) => {
        if(err)
            throw err;

        return app.emit('response', result[0].count == 1 ? true : false);
    });
});
  

Я буду признателен за любую помощь!

Комментарии:

1. Ответьте как ответ, но как прямой комментарий к вашему коду: никогда, никогда не пишите запросы к базе данных таким образом. xkcd.com/327 это смешно, потому что это правда, так что читайте npmjs.com/package/mysql#preparing-queries и примените это на практике.

2. Спасибо, сэр, за ваш ответ, я очень благодарен!

Ответ №1:

Когда вы полагаетесь на события, но не привязываете эти события к конкретному запросу, у вас будут плохие времена: прямо сейчас запросы сопоставляются с «ничем», но затем код продолжает привязывать событие, которое вызывает ответ на каждую app.on("response", ...) привязку, которую вы когда-либо объявляли … и впроцесс, также предотвращающий сбор мусора в ответах, потому что им постоянно нужно оставаться для обработки событий. Таким образом, ваш код также имеет то, что фактически является утечкой памяти.

Вместо того, чтобы делать это с событиями, используйте обещания / async функции, а затем await их возвращаемое значение. Таким образом, один запрос сопоставляется с одним ответом, и все также очищается должным образом.

server.js

 const express = require('express');
const app = express();
const database = require('./database.js');
const server = require('./server.json');

exports.app = app;

app.use(express.json());

app.post('/signin', async (req, res, next) => {
    const { username, password } = req.body;
    if (!validateUsernameAndPassword(username, password)) {
      // Remember to write a global error handler. Or don't use
      // Express' next(err) functionality but respond with a call-appropriate
      // response that has the correct HTTP error code, too.
      next(new Error("you better believe this should be an error"));
    }
    const value = await database.handleSignInRequest(username, password);
    res.send(value);
});

app.listen(server.port, () => console.log('Server running on port '   server.port));
  

database.js:

 const mysql = require('mysql');
const connectionUri = require('./db.json');
const db = mysql.createConnection(connectionUri);

// the db code shouldn't have to know anything about express or express apps

db.connect((err) => {
    if (err)
        throw err;
    console.log('MySQL connection successful!');
});

function handleSignInRequest(username, password) {
  return new Promise((resolve, reject) => {

    // Now, a super important note: NEVER QUERY A DATABASE DIRECTLY LIKE THIS:

    db.query(`SELECT Count(*) AS 'count' FROM Users WHERE (Username = '${username}' AND Password = '${password}')`, (err, result) => {
        if(err) return reject(err);
        resolve(result[0].count == 1 ? true : false);
    });

    // Look up how to query your database in a sanitized, prepared statement
    // fashion. https://xkcd.com/327/ is a word famous piece of satire for
    // good reason.
    //
    // Please read https://www.npmjs.com/package/mysql#preparing-queries and
    // put that into practice.
  });
});

module.exports = { handleSignInRequest };