W tym samouczku pokażę Ci, jak zaimplementować aplikację do czatu w czasie rzeczywistym z Node.js, Socket.IO i MongoDB, a następnie wspólnie wdrożymy tę aplikację w Modulus.
Przede wszystkim pozwól, że pokażę Ci ostateczny wygląd aplikacji, którą będziemy mieli na końcu artykułu.
Node.js będzie jądrem aplikacji, z Express jako MVC, MongoDB dla bazy danych i Socket.IO dla komunikacji w czasie rzeczywistym. Kiedy skończymy, wdrożymy naszą aplikację w Modulus. Część MongoDB faktycznie istnieje w module Modulus.
1. Scenariusz
- Jan chce korzystać z naszej aplikacji i otwiera ją w przeglądarce.
- Na pierwszej stronie wybiera pseudonim używany podczas czatu i loguje się do czatu.
- W polu tekstowym coś pisze i naciska Enter.
- Tekst jest wysyłany do usługi RESTful (Express) i jest zapisywany w MongoDB.
- Przed napisaniem w MongoDB ten sam tekst zostanie przesłany do użytkowników, którzy są aktualnie zalogowani w aplikacji czatu.
Jak widać, jest to bardzo prosta aplikacja, ale obejmuje prawie wszystko dla aplikacji internetowej. W tej aplikacji nie ma systemu kanałów, ale możesz rozwidlić kod źródłowy i zaimplementować moduł kanału do praktyki.
2. Projekt od podstaw
Postaram się najpierw wyjaśnić małe fragmenty projektu i połączyć je na końcu. Zacznę od tyłu do przodu. Zacznijmy więc od obiektów domeny (modele MongoDB).
2.1. Model
Do abstrakcji bazy danych użyjemy Mongoose. W tym projekcie mamy tylko jeden model o nazwie Message
. Ten model wiadomości zawiera tylko text
, createDate
, i author
. Nie ma modelu dla autora, takiego jak User
, ponieważ nie wdrożymy w pełni systemu rejestracji/logowania użytkowników. Powstanie prosta strona z pseudonimem, która zostanie zapisana w pliku cookie. Będzie to użyte w Message
model jako tekst w author
pole. Poniżej możesz zobaczyć przykładowy model JSON:
{ text: "Hi, is there any Full Stack Developer here?" author: "john_the_full_stack", createDate: "2015.05.15" }
Aby tworzyć takie dokumenty, możesz zaimplementować model, korzystając z poniższych funkcji Mongoose:
var mongoose = require('mongoose') var Message = new mongoose.Schema({ author: String, message: String, createDate: { type: Date, default: Date.now } }); mongoose.model('Message', Message)
Po prostu zaimportuj moduł Mongoose, zdefiniuj swój model z jego polami i atrybutami pól w formacie JSON i utwórz model o nazwie Message
. Ten model zostanie uwzględniony na stronach, których chcesz użyć.
Może masz pytanie, dlaczego przechowujemy wiadomość w bazie danych, skoro już nadajemy tę wiadomość użytkownikowi na tym samym kanale. Co prawda nie musisz przechowywać wiadomości na czacie, ale chciałem tylko wyjaśnić warstwę integracji bazy danych. Zresztą w naszym projekcie użyjemy tego modelu wewnątrz kontrolerów. Kontrolery?
2.2. Kontroler
Jak powiedziałem wcześniej, użyjemy Express dla części MVC. I C
tutaj oznacza Controller
. W naszych projektach będą tylko dwa punkty końcowe do przesyłania wiadomości. Jeden z nich służy do wczytywania ostatnich wiadomości czatu, a drugi do obsługi wysłanych wiadomości czatu do przechowywania w bazie danych, a następnie nadawania na kanał.
..... app.get('/chat', function(req, res){ res.sendFile(__dirname + '/index.html'); }); app.get('/login', function(req, res){ res.sendFile(__dirname + '/login.html'); }); app.post('/messages', function(req, res, next) { var message = req.body.message; var author = req.body.author; var messageModel = new Message(); messageModel.author = author; messageModel.message = message; messageModel.save(function (err, result) { if (!err) { Message.find({}).sort('-createDate').limit(5).exec(function(err, messages) { io.emit("message", messages); }); res.send("Message Sent!"); } else { res.send("Technical error occurred!"); } }); }); app.get('/messages', function(req, res, next) { Message.find({}).sort('-createDate').limit(5).exec(function(err, messages) { res.json(messages); }); }); .....
Pierwszy i drugi kontroler służą tylko do obsługi statycznych plików HTML dla stron czatu i logowania. Trzecia służy do obsługi żądania poczty do /messages
punkt końcowy do tworzenia nowych wiadomości. W tym kontrolerze najpierw treść żądania jest konwertowana na model Message, a następnie model ten jest zapisywany w bazie danych za pomocą funkcji Mongoose save
.
Nie będę się zbytnio zagłębiać w Mongoose — więcej szczegółów znajdziesz w dokumentacji. Możesz udostępnić funkcję zwrotną dla funkcji zapisywania, aby sprawdzić, czy jest jakiś problem, czy nie. Jeśli się powiedzie, pobraliśmy ostatnie pięć rekordów posortowanych w porządku malejącym według createDate
i wyemitowali pięć komunikatów do klientów w kanale.
OK, zakończyliśmy MC
. Przejdźmy do View
część.
2.3. Zobacz
Ogólnie rzecz biorąc, w Express można używać silnika szablonów, takiego jak Jade, EJS, Handlebars itp. Mamy jednak tylko jedną stronę i jest to wiadomość na czacie, więc będę ją obsługiwać statycznie. W rzeczywistości, jak wspomniałem powyżej, istnieją jeszcze dwa kontrolery obsługujące tę statyczną stronę HTML. Możesz zobaczyć następujące informacje dotyczące obsługi statycznej strony HTML.
app.get('/chat', function(req, res){ res.sendFile(__dirname + '/index.html'); }); app.get('/login', function(req, res){ res.sendFile(__dirname + '/login.html'); });
Ten punkt końcowy po prostu obsługuje index.html i login.html za pomocą res.sendFile
. Oba index.html i login.html znajdują się w tym samym folderze co server.js, dlatego użyliśmy __dirname
przed nazwą pliku HTML.
2.4. Frontend
Na stronie front-endowej użyłem Bootstrapa i nie ma potrzeby wyjaśniać, jak mi się to udało. Po prostu powiązałem funkcję z polem tekstowym i za każdym razem, gdy naciśniesz Enter klawisz lub Wyślij przycisk, wiadomość zostanie wysłana do usługi zaplecza.
Ta strona zawiera również wymagany plik js Socket.IO do nasłuchiwania kanału o nazwie message
. Moduł Socket.IO jest już zaimportowany w back-endzie, a gdy używasz tego modułu po stronie serwera, automatycznie dodaje punkt końcowy do obsługi pliku Socket.IO js, ale używamy tego, który jest obsługiwany z cdn <script src="//cdn.socket.io/socket.io-1.3.5.js"></script>
. Za każdym razem, gdy na tym kanale pojawi się nowa wiadomość, zostanie ona automatycznie wykryta, a lista wiadomości zostanie odświeżona o pięć ostatnich wiadomości.
<script> var socket = io(); socket.on("message", function (messages) { refreshMessages(messages); }); function refreshMessages(messages) { $(".media-list").html(""); $.each(messages.reverse(), function(i, message) { $(".media-list").append('<li class="media"><div class="media-body"><div class="media"><div class="media-body">' + message.message + '<br/><small class="text-muted">' + message.author + ' | ' + message.createDate + '</small><hr/></div></div></div></li>'); }); } $(function(){ if (typeof $.cookie("realtime-chat-nickname") === 'undefined') { window.location = "/login" } else { $.get("/messages", function (messages) { refreshMessages(messages) }); $("#sendMessage").on("click", function() { sendMessage() }); $('#messageText').keyup(function(e){ if(e.keyCode == 13) { sendMessage(); } }); } function sendMessage() { $container = $('.media-list'); $container[0].scrollTop = $container[0].scrollHeight; var message = $("#messageText").val(); var author = $.cookie("realtime-chat-nickname"); $.post( "/messages", {message: message, author: author}, function( data ) { $("#messageText").val("") }); $container.animate({ scrollTop: $container[0].scrollHeight }, "slow"); } }) </script>
W powyższym kodzie jest jeszcze jedno sprawdzenie:część dotycząca plików cookie. Jeśli nie wybrałeś żadnego pseudonimu do czatu, oznacza to, że plik cookie nie jest ustawiony dla pseudonimu i zostaniesz automatycznie przekierowany na stronę logowania.
Jeśli nie, ostatnie pięć wiadomości zostanie pobranych przez proste wywołanie Ajax do /messages
punkt końcowy. W ten sam sposób za każdym razem, gdy klikniesz Wyślij lub naciśnij Enter klucz, wiadomość tekstowa zostanie pobrana z pola tekstowego, a pseudonim zostanie pobrany z pliku cookie, a te wartości zostaną wysłane na serwer z żądaniem post. Nie ma tutaj ścisłego sprawdzania pseudonimu, ponieważ chciałem skupić się na części w czasie rzeczywistym, a nie na części dotyczącej uwierzytelniania użytkownika.
Jak widać, ogólna struktura projektu jest bardzo prosta. Przejdźmy do części wdrożeniowej. Jak powiedziałem wcześniej, użyjemy modułu Modulus, jednego z najlepszych PaaS do wdrażania, skalowania i monitorowania aplikacji w wybranym przez Ciebie języku.
3. Wdrożenie
3.1. Wymagania wstępne
Pierwszą rzeczą, która przychodzi mi do głowy, jest pokazanie, jak wdrożyć, ale do pomyślnego wdrożenia potrzebujemy działającej bazy danych. Przyjrzyjmy się, jak utworzyć bazę danych w module Modulus, a następnie przeprowadzić wdrożenie.
Po utworzeniu konta przejdź do pulpitu nawigacyjnego Modulus. Kliknij Bazy danych po lewej stronie i kliknij Utwórz bazę danych.
Wypełnij wymagane pola w wyskakującym formularzu, jak poniżej.
Gdy wypełnisz wymagane pola i klikniesz Utwórz, utworzy dla ciebie bazę danych MongoDB, a na ekranie zobaczysz adres URL bazy danych. Użyjemy MONGO URI , więc skopiuj ten URI.
W naszym projekcie Mongo URI jest pobierane ze zmiennej środowiskowej MONGO_URI
i musisz ustawić tę zmienną środowiskową na pulpicie nawigacyjnym. Przejdź do panelu, kliknij Projekty wybierz swój projekt z listy i kliknij Administracja w lewym menu. Na tej stronie zobaczysz sekcję zmiennych środowiskowych podczas przewijania strony w dół, jak pokazano poniżej.
Wdrożenie w module Modulus można przeprowadzić na dwa sposoby:
- przesyłanie pliku ZIP projektu za pomocą pulpitu nawigacyjnego
- wdrażanie z wiersza poleceń za pomocą Modulus CLI
Będę kontynuował opcję wiersza poleceń, ponieważ druga jest łatwa do zrobienia. Przede wszystkim zainstaluj Modulus CLI:
npm install -g modulus
Przejdź do folderu projektu i wykonaj następujące polecenie, aby zalogować się do modułu Modulus.
modulus login
Po wykonaniu powyższego polecenia zostaniesz poproszony o podanie nazwy użytkownika i hasła:
Jeśli utworzyłeś konto za pomocą GitHub, możesz użyć --github
opcja.
modulus login --github
Teraz jesteś zalogowany do Modulus i nadszedł czas na stworzenie projektu. Użyj następującego polecenia, aby utworzyć projekt:
modulus project create "Realtime Chat"
Po uruchomieniu tej funkcji zostaniesz zapytany o czas działania. Wybierz pierwszą opcję, czyli Node.js, a drugą zostaniesz zapytany o rozmiar serwa i możesz zachować go jako domyślny.
Stworzyliśmy projekt i tym razem wdrożymy nasz obecny projekt do Modulus. Wykonaj następujące polecenie, aby wysłać bieżący projekt do czatu w czasie rzeczywistym projekt po stronie modułu.
modulus deploy
Wdroży on Twój projekt, a na końcu wiadomości o pomyślnym wdrożeniu otrzymasz adres URL bieżącego projektu:
Realtime Chat running at realtime-chat-46792.onmodulus.net
Jak widać, wdrożenie do Modulus jest bardzo proste!
Modulus CLI zawiera bardzo przydatne polecenia do użycia podczas wdrażania projektu lub w czasie wykonywania. Na przykład, aby śledzić logi działającego projektu, możesz użyć modulus project logs tail
, aby utworzyć bazę danych MongoDB użyj modulus mongo create <db-name>
, aby ustawić zmienną środowiskową użyj modulus env set <key> <value>
itp. Pełną listę poleceń można wyświetlić, korzystając z pomocy programu Modulus.