socket.io 在我刷新页面之前输出未定义,然后显示?

socket.io output is undefined until I refresh the page, then it shows up?

好吧,我被难住了。

我一直在学习一些教程,并成功地在 nodejs/express 中创建了一个基本用户 registration/login 应用程序。我还添加了 socket.io 聊天。当用户登录时,他们会被定向到聊天页面,他们可以在其中进行实时聊天。

我目前已将其设置为当用户发布消息时,我们从他们的会话 cookie 中获取他们的用户名,然后在将消息插入 mongoDB 时使用它。发送新消息时,套接字会将其发送给所有连接到聊天的人。

我的问题是,当我发送消息时,它最初在屏幕上显示为 "undefined: message here"。如果我随后刷新整个页面,实际用户名将显示为:"Mike: message here"。我在提交例程期间在控制台记录了用户名,它确实显示了,所以我很困惑为什么在我发送消息时它不会打印在页面上。

这是我发送消息时的截图:

如果我刷新页面,它会是这样的:

这是我的app.js:

var mongo = require('mongodb').MongoClient;
var bodyParser = require('body-parser');
var bcrypt = require('bcryptjs');
var csrf = require('csurf');
var path = require ('path');
var express = require('express');
var mongoose = require('mongoose');
var uniqueValidator = require('mongoose-unique-validator');
var ios = require('socket.io-express-session');




var moment = require('moment');
var now = moment().format('L');
var http = require('http');
var connect = require('connect');
var secret = 'mysecret';
var cookie = require('cookie');
var cookieParser = require('cookie-parser');
var session = require('express-session')({

    secret: 'mysecret',
    resave:false,
   saveUninitialized: false,
    stringify:true,
    httpOnly: false

});
 var sharedsession = require("express-socket.io-session");
//var MongoStore = require('connect-mongo')(session);


var Schema = mongoose.Schema;
var ObjectId = Schema.ObjectId;

UserSchema = new Schema({
    //id: ObjectId,

    firstName: String,
    lastName: String,    
    username: {
        type: String,
        unique: true,
        uniqueCaseInsensitive:true
    },
    password: String,
    email: {
        type:String,
         unique: true,
        uniqueCaseInsensitive:true
    },
    accountType: String,
    accountStatus: String,
    acctActivation:{ 
        type:String,
    unique:true
},
joinDate: String

});
UserSchema.plugin(uniqueValidator,{ message: 'Error,  {PATH} {VALUE} has already been registered.\r' });
var User = mongoose.model('User', UserSchema);

var app = express();
app.engine('ejs', require('ejs').renderFile);

app.locals.pretty = true;

//connect to mongo
mongoose.connect('mongodb://localhost/userDB');

//create server

var server = http.createServer(app).listen(3000);
var io = require('socket.io')(server);
console.log('listening on port 3000');


//middleware

app.use(express.static('public'));
app.use(bodyParser.urlencoded({extended:true}));


app.use(session);
  io.use(sharedsession(session, {
  autoSave: true
}));


app.use(csrf());

app.use(function(req,res,next){ // check to see if user already has a session, if so, query mongodb and update the user object
    if(req.session && req.session.user){
        User.findOne({email: req.session.user.email}, function(err, user){
            if(user){
                req.user = user;
                delete req.user.password; // remove password field from session

                req.session.user = req.user;
                res.locals.user = req.user;
            }
            next();
        });
    }else{
        next(); 
    }
});

function requireLogin(req,res,next){ //  check to see if user is logged in, if not, boot em
    if(!req.user){
        res.redirect('/login');
    }else{
        next();
    }
};
function requireAdmin(req,res,next){ // check to see if accountType = Developer (or admin later) - if not, send them to dashboard
    if(req.user.accountType !== 'Developer'){
        res.redirect('/dashboard');
    }else{
        next();
    }
}; 
app.get('/', function(req, res){
    if(req.user){
          res.render('dashboard.ejs');
    }else{
          res.render('index.ejs');
    }

});

app.get('/register', function(req,res){    
    res.render('register.ejs', {csrfToken: req.csrfToken(),
    error:false});
});

app.post('/register', function(req,res){        
    var hash = bcrypt.hashSync(req.body.password, bcrypt.genSaltSync(10));

    var user = new User({
        firstName: req.body.firstName,
        lastName: req.body.lastName,
        username: req.body.username,
        password: hash,
        email: req.body.email,
        accountType: 'Standard',
        accountStatus: 'Active',        
        joinDate: now
    });

    user.save(function(err){
        if(err){
            console.log(err);

            res.render('register.ejs', {csrfToken: req.csrfToken(),
                error: err});

    }else{
       req.session.user = user; //set-cookie
        res.redirect('/dashboard');
    }

    }); 

});

app.get('/login', function(req,res){

    res.render('login.ejs', {
        csrfToken: req.csrfToken(),error:false});
});


app.post('/login', function(req, res){
    User.findOne({username: {$regex: new RegExp('^' + req.body.username, 'i')}}, function(err, user){
        if(!user){

            res.render('login.ejs', {error: 'Invalid username or password combination.',
             csrfToken: req.csrfToken()});
        }else{
            if(bcrypt.compareSync(req.body.password, user.password)){

                req.session.user = user; //set-cookie

                res.redirect('/chat');
            }else{

                res.render('login.ejs', {error: 'Invalid username or password combination.',
                 csrfToken: req.csrfToken()});
            }
        }
    });
});

app.get('/dashboard', requireLogin, function(req,res){   
       res.render('dashboard.ejs');
});
app.get('/chat', requireLogin, function(req,res){   
       res.render('chat.ejs');
});
app.get('/admin', requireLogin, requireAdmin, function(req,res){   //required logged in AND admin status
  // var userlist = User.find({});

   User.find({},{},function(err,docs){

       res.render('admin.ejs',{ "userlist": docs

       }); 

   }) ;



      // res.render('admin.ejs');
});

app.get('/logout', function(req,res){
    req.session.destroy();
    res.redirect('/');
});

mongo.connect('mongodb://127.0.0.1/chat', function(err,db){
    if(err) throw err; 




io.on('connection', function(socket){

var userData = socket.handshake.session;

 var chatUserName = userData.user.username;

  socket.emit('status', 'chatting as '+ chatUserName);
    var col = db.collection('messages');
    sendStatus = function(s){
      socket.emit('status', s);  
    }; 

    //emit all messages (shows old room data)
    col.find().sort({_id: -1}).limit(100).toArray(function(err, res){ 
        if(err) throw err;

        socket.emit('output',res);


    }); 

    //wait for input
    socket.on('input', function(data){

      name = chatUserName,
      message = data.message,
      whitespacePattern = /^\s*$/;

      if(whitespacePattern.test(message)){
          sendStatus('You cannot send an empty message');
      }else{
          col.insert({name: chatUserName, message: message}, function(){
              //emit latest message to all clients 

              io.emit('output', [data]);

         sendStatus({
             message: "Message sent",
             clear: true
         });

          });  
      }

    });

});

});

这是我的 chat.ejs 文件,它呈现聊天页面:

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Test </title>

        <link rel="stylesheet" href="/css/main.css">


        </head>
     <body>
<h1>Chat</h1>

<div class="chat">

    <div class="chat-messages"><div class="chat-message"></div></div>
    <textarea class="chat-textarea" placeholder="type your message"></textarea>
    <div class="chat-status">Status: <span>Idle</span></div>

</div>
<script src="http://example.com:3000/socket.io/socket.io.js"></script>
<script>
    (function(){
        var getNode = function(s){
            return document.querySelector(s);
        },
        // get required nodes
        status = getNode('.chat-status span'),
        messages = getNode('.chat-messages');
        textarea = getNode('.chat-textarea'),

        statusDefault = status.textContent,


        setStatus = function(s){
            status.textContent = s;

            if(s !== statusDefault){
                var delay = setTimeout(function(){
                  setStatus(statusDefault);
                  clearInterval(delay);
                },3000);
            }
    };

        try{
            var socket = io.connect('http://example.com:3000');
        }catch(e){
            //set status to warn user

        }

        if(socket !== undefined){
            //listen for output

            socket.on('output', function(data){
                data.reverse();
                if(data.length){
                    //loop through results
                    for(var x = 0;x< data.length;x++){

                      var message = document.createElement('div');
                      message.setAttribute('class', 'chat-message');
                       message.textContent = data[x].name + ': ' + data[x].message;

                        //append
                       messages.insertBefore(message, messages.firstChild);
                       messages.appendChild(message);
                       messages.scrollTop = messages.scrollHeight;
          }

                }
            });

            //listen for status
           socket.on('status', function(data){
               setStatus((typeof data === 'object') ? data.message : data);

            if(data.clear === true){
                textarea.value = '';
            }
           });
            //listen for keydown

            textarea.addEventListener('keydown', function(event){
                var self = this;


                if(event.which === 13 && event.shiftKey === false){ //13 is enter, shifKey is shift
                   //send the data
                   socket.emit('input', {

                       message: self.value
                   });

                   event.preventDefault();
                }
            });
        }
    })();
    </script>

如果有人能指出我遗漏了什么,我将不胜感激!我知道我需要将我的代码分成模块等并清理它——但我试图在这样做之前修复这部分。我是 node 和 express 的新手,所以请原谅我对这一切的无知。

非常感谢您的宝贵时间

我很确定你的问题是这样的:

io.emit('output', [data]);

在新输入中,您的数据仅包含消息:

socket.emit('input', { 
    message: self.value
});

刷新时,您将从数据库中获取值,您在其中正确存储了用户名。

所以基本上,您可以在发回数据之前将 chatUserName 附加到数据,这可能会解决您的问题。

data.name = chatUserName;
io.emit('output', [data]);