Recently, I want to make a Node.js application to initially implement the Node.js skills I have learned. After thinking about it, I would like to make a small chat application. I am afraid that I don’t have enough energy in the later stage of blog (before DOING phper, I gave up 😅 in the middle of doing blog because I thought more and more about its functions). Since I have never done WebSocket related business before, I also want to practice in this aspect. I expect to make a small function with single chat and group chat, simple circle of friends and so on. At the same time, I also want to practice in this aspect because I have never used Mongo before.
In short, this small application is mainly personal exploration of Node “full stack” preliminary test water, but also want to share with me as still vegetables front-end.
Initial functions to be implemented
-
Registration & login required
-
Notify the current user online or offline information to the user and other users
-
Distinguish message types, online information, and messages from clients
-
The message is sent to the target user
-
Initial implementation of a dialogue between two users
For the moment, assume that you have user user1, and user2 implements the conversation between the two users
Required plug-ins and dependencies
Express 4.17.1
Glob 7.1.6
Mongoose 5.9.14
Ejs 3.1.2
Nodemon 2.0.4
Ws 7.3.0
Front-end Websocket communication implementation
const ws = new WebSocket('ws: / / 192.168.31.200:8000') // Connect to websokect Server
let userInfo = JSON.parse(sessionStorage.getItem("userInfo")); // Current user information
const JSONToString = function(json) {
return JSON.stringify(json)
}
// Connect to the server and submit the current user information to the server
ws.onopen = (a)= > {
// ws.send(' I'm online '); // ws.send(' I'm online '
ws.send(JSON.stringify({
sender: userInfo.name
}))
}
// Receive messages from the server
ws.onmessage = (msg) = > {
// Handle logic based on the MSG type returned by server, notify other users, render messages, etc
// MSG. MsgType is classified as notice message
//TODO
}
// Server communication error handling
ws.onerror = err= > {
console.log(err)
//TODO
}
// Offline logic
ws.onclose = (a)= > {
ws.send(JSON.stringify({
sender: userInfo.name,
receiver: userInfo.name == 'user1' ? 'test2' : "user1".message: msg
}))
console.log('close')}// Send a message to the server. Other events call this method
function sendMsgToServer(msg) {
// MSG temporary format
/ / {
// sender: userInfo.name,
// receiver: receiver,
// message: MSG Note that there are more messages than the first onopen
/ /}
ws.send(JSONToString(msg))
}
Copy the code
Setting up the Express Service
The directory structure
—common
|—function.js
—db
|—mongo.conf.js
— routes
|—user.js
—views
|—login.html
|—chating.html
app.js
Introduce basic modules and enable services
//app.js
const express = require('express')
const app = express()
const glob = require("glob");
require('./routes/chats')
const {
resolve
} = require('path');
app.listen(3000)
console.log('Service started')
Copy the code
Configuring the Template Engine
//app.js
/* express.js: configure the engine */
app.set('views'.'./views'); // Add view path
app.engine('html'.require('ejs').renderFile); // Map the EJS template to the ".html" file
app.set('view engine'.'html'); // Set the view engine
/* express.js: configure the engine */
glob.sync(resolve('./views'."**/*.html")).forEach((item, i) = > {
let htmlRelativePath = item.split('/views') [1]
let pagePath = htmlRelativePath.replace('.html'.' ')
app.get(pagePath, function (request, response) {
let viewPath = pagePath.replace('/'.' ')
response.render(viewPath)
})
})
Copy the code
Express parses the configuration required for jSON-formatted request parameters
//app.js
app.use(express.json())
app.use(express.urlencoded({
extended: true
}))
Copy the code
Add the routing
//app.js
const userRouter = require('./routes/user')
app.use('/', userRouter)
Copy the code
Mongo basic configuration
const mongoose = require('mongoose') / / into the mongoose
const url = "mongodb://localhost:27017/chat"; // Address of the local database
mongoose.connect(url)
const db = mongoose.connection;
db.on('error'.console.error.bind(console.'connection error:'));
db.once('open'.function () {
console.log("Successful connection to " + url)
});
var Schema = mongoose.Schema
let user = {
name: String.password: String.headImg: String
}
var userSchema = Schema(user)
var User = mongoose.model('users', userSchema); // Compile the schema into the model constructor
module.exports = {
mongoose,
User
}
// This configuration is still a little rough, later to modify 😥
Copy the code
User module function realization
//user.js
const express = require('express')
const router = express.Router()
const ObjectID = require('mongodb').ObjectID;
const {
sendJson,
throwError
} = require('.. /common/function')
const {
mongoose,
User
} = require(".. /db/mongo.conf")
// Don't worry about this for the moment, because at this point I only focus on the main function 😅
const checkUserExit = function (params) {
return new Promise(function (resolve, reject) {
User.findOne(params, function (error, res) {
if(res) {
resolve(res)
}
})
})
}
/ / register
router.post('/register'.function (request, response) {
let params = request.body
const user = new User(params)
checkUserExit({
name: params.name
}).then(res= > {
if (res) {
response.send(sendJson(0.'Username already exists'))}else {
user.save(function (error, res) {
if (error) {
response.send(throwError())
} else {
response.send(sendJson(1.'Registration successful'))}})}})/ / login
router.post('/login'.function (request, response) {
let params = request.body
User.findOne({
name: params.name
}, function (error, res) {
if(! res) { response.send(sendJson(0.'User does not exist'))}else {
if(params.password ! = res.password) { response.send(sendJson(0.'Wrong username or password'))}else {
response.send(sendJson(1.'User authentication succeeded',params))
}
}
})
})
module.exports = router
Copy the code
At present, public method encapsulation is not much, or to achieve normal flow
//function.js
const getJsonStr = function (params) {
return JSON.stringify(params)
}
function sendJson(status, msg, data, params) {
return getJsonStr({
status: status,
message: msg,
data: data || null})}function throwError(params) {
return getJsonStr({
status: 0.msg: 'Service error'})}module.exports.sendJson = sendJson
module.exports.throwError = throwError
Copy the code
Websocket Server basic implementation
Step 1 Start the service
const webSocket = require('ws'); // Import the WS server module
const ws = new webSocket.Server({
port: 8000
}); // Create a server with port 8000
const {
JSONToString,
getTime
} = require('.. /common/function')
var clients = {} // Records the information about the current online user
var userList = [] // Only the current online user name is stored
Copy the code
Step 2 Connect to the service and interact with the client
ws.on('connection', (client) => { // Connect to the client
// The user goes online
client.on('message', (msg) => {
let userMsg = JSON.parse(msg)
let {
sender,
receiver,
message
} = userMsg
client.name = sender;
Observer() // Update basic data in real time
if (message) {
// Send data output
sendMessageToClient(sender, receiver, message)
} else {
// Notifications go live
noticeOnlineOrOffLine(sender, true)}})// An error message was reported
client.on('error', (err= > {
if (err) {
console.log(err)
// I haven't decided what to do}}))// Offline information
client.on('close', () = > {console.log('users' + client.name + 'Message service closed')
noticeOnlineOrOffLine(client.name, false)})})Copy the code
Step 3. Send the MESSAGE to the specified user
/** * * @param {*String} sender * @param {*String} receiver * @param {*Object} message * @param {*Boolean} isOnline */
const sendMessageToClient = function (sender, receiver, message) {
let messageInfo = {
sender: sender,
message: message,
msgType: "message".timestamp: getTime(),
userList: userList
}
// If the receiver is online, send to it
if (receiver) {
messageInfo.receiver = receiver
clients[receiver].send(JSONToString(messageInfo))
}
clients[sender].send(JSONToString(messageInfo))
console.log('Send a message to a client', JSONToString(messageInfo))
}
Copy the code
Step 4 Notify other users of the current online status
/** * * @param {*String} currentUser * @param {*Boolean} isOnline */
const noticeOnlineOrOffLine = function (currentUser, isOnline) {
for (var key in clients) {
// You need to update the friends list of other users
let noticeUserMessage = {}
let exceptCurrentUserList = userList.filter(el= >el ! = currentUser) noticeUserMessage =Object.assign(onlineOrOffLineNoticeMsg(key, isOnline), {
userList: isOnline ? userList : exceptCurrentUserList
})
let isOnlineMsg = isOnline ? 'online' : 'off'
console.log('users: + currentUser + isOnlineMsg + ', message :' + JSONToString(noticeUserMessage))
clients[key].send(JSONToString(noticeUserMessage))
}
if(! isOnline) {deleteclients[currentUser]; }}Copy the code
// Offline message template
const onlineOrOffLineNoticeMsg = function (receiver, isOnline) {
return {
receiver: receiver,
msgType: 'notice'.message: isOnline ? receiver + 'Online' : receiver + 'Offline'.timestamp: getTime()
}
}
Copy the code
At this point, the main function of this small application is basically perfect, the first step of the long March, ha ha 😁, due to the current just to chat the process go through, even the interface is literally written a few div (and not can not use, manual dog head), you may have found that mongo has not been used to chat in the process 🤣, Because the current mongo enabled posture is not deep enough, I am afraid to dig a hole for myself, and I will do data after further planning.
The main function that needs to be improved in the later stage is group chat (select the kind of fixed users, not everyone’s chat room), followed by the realization of the circle of friends function, involving data storage, graphic processing and so on, but also need to plan and polish, will be further updated.
Finally, due to my limited level, and may use a relatively bad way of business implementation, I hope not to mislead beginners, also please various gods for correction, suggestions and exchanges.
Attached github address tiny-chat