Tworzenie podstawowej aplikacji internetowej

Na tym etapie na maszynie wirtualnej z systemem Ubuntu zainstalowane są baza danych MongoDB i środowisko Node.js. Teraz utworzysz podstawową aplikację internetową, aby zobaczyć, jak to działa. Zobaczysz też, jaka jest rola rozwiązań AngularJS i Express.

Najlepiej uczymy się na przykładach. W aplikacji internetowej, którą utworzysz, zostanie wdrożona podstawowa baza danych książek. Ta aplikacja internetowa umożliwia wyświetlanie listy informacji o książkach, dodawanie nowych książek i usuwanie książek.

W tej aplikacji internetowej przedstawiono wiele koncepcji mających zastosowanie w większości aplikacji internetowych opartych na stosie MEAN. W zależności od swoich potrzeb lub zainteresowań możesz zapoznać się z funkcjami, których potrzebujesz do tworzenia własnych aplikacji opartych na stosie MEAN.

Oto jak będzie wyglądać aplikacja internetowa z informacjami o książkach.

Strona internetowa z formularzem i przyciskiem przesyłania.

Poniżej przedstawiono rolę, jaką odgrywają poszczególne składniki stosu MEAN.

  • Bazy danych MongoDB przechowuje informacje o książkach.
  • Rozwiązanie Express kieruje poszczególne żądania HTTP do odpowiednich procedur obsługi.
  • Rozwiązanie AngularJS łączy interfejs użytkownika z logiką biznesową programu.
  • Środowisko Node.js hostuje aplikację po stronie serwera.

Ważne

Utworzysz teraz podstawową aplikację internetową dla celów szkoleniowych. Umożliwia ona przetestowanie stosu MEAN i zapoznanie się ze sposobem jego działania. Ta aplikacja nie jest wystarczająco bezpieczna ani gotowa do użycia w środowisku produkcyjnym.

Co z rozwiązaniem Express?

Do tej pory na maszynie wirtualnej zainstalowano bazę danych MongoDB i środowisko Node.js. A co z rozwiązaniem Express, do którego odnosi się litera E w nazwie MEAN?

Express to struktura serwera internetowego dla środowiska Node.js, która upraszcza proces tworzenia aplikacji internetowych.

Jej głównym celem jest obsługa kierowania żądań. Kierowanie żądań oznacza sposób, w jaki aplikacja odpowiada na żądania przesłane do określonego punktu końcowego. Punkt końcowy zawiera ścieżkę, inaczej identyfikator URI, oraz metodę żądania, na przykład GET lub POST. Na przykład w odpowiedzi na żądanie GET do punktu końcowego /book może być zwracana lista wszystkich książek w bazie danych. Natomiast w odpowiedzi na żądanie POST do tego samego punktu końcowego może być dodawany wpis bazy danych zawierający informacje wprowadzone przez użytkownika w polach formularza internetowego.

W aplikacji internetowej, którą niedługo utworzysz, rozwiązanie Express będzie używane do kierowania żądań HTTP i zwracania zawartości internetowej do użytkownika. Rozwiązanie Express może również ułatwić korzystanie z plików cookie HTTP i przetwarzanie ciągów zapytań w aplikacjach internetowych.

Rozwiązanie Express jest pakietem środowiska Node.js. Narzędzie npm dostarczane wraz ze środowiskiem Node.js umożliwia instalowanie pakietów Node.js i zarządzanie nimi. Później w tej części modułu utworzysz plik o nazwie package.json w celu zdefiniowania rozwiązania Express i innych zależności, a następnie uruchomisz polecenie npm install, aby zainstalować te zależności.

Co z rozwiązaniem AngularJS?

Podobnie jak rozwiązanie Express, rozwiązanie AngularJS, do którego odnosi się litera A w nazwie „MEAN”, nie zostało jeszcze zainstalowane.

Rozwiązanie AngularJS ułatwia pisanie i testowanie aplikacji internetowych, ponieważ umożliwia lepsze oddzielenie wyglądu strony internetowej, kodu HTML, od jej działania. Jeśli znasz wzorzec MVC (Model-View-Controller) lub koncepcję powiązań danych, rozwiązanie AngularJS będzie dla Ciebie znajome.

Rozwiązanie AngularJS to tak zwana struktura JavaScript frontonu, co oznacza, że musi być dostępne tylko w środowisku klienckim uzyskującym dostęp do aplikacji. Innymi słowy rozwiązanie AngularJS działa w przeglądarce internetowej użytkownika, a nie na Twoim serwerze internetowym. Ponieważ rozwiązanie AngularJS bazuje na języku JavaScript, możesz za jego pomocą łatwo pobierać z serwera internetowego dane do wyświetlenia na stronie.

Tak naprawdę nie trzeba instalować platformy AngularJS. Wystarczy dodać odwołanie do pliku JavaScript na stronie HTML, tak samo jak w przypadku innych bibliotek języka JavaScript. Istnieje kilka sposobów na dodanie rozwiązania AngularJS do stron internetowych. Teraz załadujesz rozwiązanie AngularJS z poziomu sieci dostarczania zawartości (sieci CDN). Sieci CDN umożliwiają rozproszenie geograficzne obrazów, klipów wideo i innych treści w celu przyspieszenia ich pobierania.

Nie dodawaj jeszcze tego kodu — to przykład ładowania rozwiązania AngularJS z poziomu sieci CDN. Na ogół ten kod dodawany jest w sekcji <head> strony HTML.

<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.7.2/angular.min.js"></script>

Uwaga

Nie należy mylić rozwiązania AngularJS z usługą Angular. Choć używane są w nich podobne koncepcje, rozwiązanie AngularJS jest wcześniejszą wersją usługi Angular. Rozwiązanie AngularJS jest nadal powszechnie używane do tworzenia aplikacji internetowych. Rozwiązanie AngularJS bazuje na języku JavaScript, natomiast usługa Angular — na języku TypeScript, który ułatwia tworzenie programów w języku JavaScript.

Jak skompilować aplikację?

Tutaj zastosujesz podstawowy proces. Utworzysz kod aplikacji w usłudze Cloud Shell, a następnie skopiujesz pliki na maszynę wirtualną za pomocą protokołu SCP. Następnie uruchomisz aplikację Node.js i zobaczysz wyniki w przeglądarce.

W praktyce na ogół tworzy się i testuje aplikacje internetowe w bardziej lokalnym środowisku, na przykład na laptopie lub uruchomionej lokalnie maszynie wirtualnej. Następnie można zapisać kod w systemie kontroli wersji, takim jak Git, i zastosować system ciągłej integracji/ciągłego wdrażania (CI/CD) taki jak Azure DevOps do testowania zmian i przekazywania ich na maszynę wirtualną. Na końcu tego modułu znajdziesz linki do dodatkowych zasobów.

Tworzenie aplikacji internetowej z informacjami o książkach

W tej części utworzysz kod, skrypty i pliki HTML, składające się na aplikację internetową. Dla zwięzłości omówimy najważniejsze elementy każdego pliku, ale bez wszystkich szczegółów.

Jeśli nadal masz połączenie SSH z maszyną wirtualną, uruchom polecenie exit, aby zakończyć sesję SSH i powrócić do usługi Cloud Shell.

exit

Wrócisz w ten sposób do sesji usługi Cloud Shell.

Tworzenie plików

  1. W usłudze Cloud Shell uruchom następujące polecenia, aby utworzyć foldery i pliki dla aplikacji internetowej.

    cd ~
    mkdir Books
    touch Books/server.js
    touch Books/package.json
    mkdir Books/app
    touch Books/app/model.js
    touch Books/app/routes.js
    mkdir Books/public
    touch Books/public/script.js
    touch Books/public/index.html
    

    Są to:

    • Books — katalog główny projektu.
      • Plik server.js definiuje punkt wejścia do aplikacji internetowej. Ładuje wymagane pakiety Node.js, określa port nasłuchiwania i rozpoczyna nasłuchiwanie przychodzącego ruchu HTTP.
      • Plik package.json zawiera informacje o aplikacji, w tym jej nazwę, opis oraz pakiety Node.js, których aplikacja potrzebuje do działania.
    • Books/app — zawiera kod, który działa na serwerze.
      • Plik model.js definiuje połączenie z bazą danych i schemat. Można traktować go jako model danych dla aplikacji.
      • Plik routes.js obsługuje kierowanie żądań. Na przykład definiuje żądanie GET do punktu końcowego /book jako żądanie zwrócenia listy wszystkich książek w bazie danych.
    • Books/public — zawiera pliki, które są dostarczane bezpośrednio do przeglądarki klienta.
      • Plik index.html zawiera stronę indeksu. Na tej stronie znajduje się formularz internetowy umożliwiający użytkownikowi przesyłanie informacji o książkach. Pozwala także wyświetlać wszystkie książki w bazie danych i usuwać wpisy z bazy.
      • Plik script.js zawiera kod JavaScript, który działa w przeglądarce użytkownika. Umożliwia wysyłanie do serwera żądań zwrócenia listy książek, dodania książek do bazy danych i usunięcia książek z bazy danych.
  2. Uruchom polecenie code, aby otworzyć pliki za pomocą edytora usługi Cloud Shell.

    code Books
    

Tworzenie modelu danych

  1. W edytorze otwórz plik app/model.js i dodaj następujący kod.

    var mongoose = require('mongoose');
    var dbHost = 'mongodb://localhost:27017/Books';
    mongoose.connect(dbHost, { useNewUrlParser: true } );
    mongoose.connection;
    mongoose.set('debug', true);
    var bookSchema = mongoose.Schema( {
        name: String,
        isbn: {type: String, index: true},
        author: String,
        pages: Number
    });
    var Book = mongoose.model('Book', bookSchema);
    module.exports = Book;
    

    Ważne

    Zawsze, gdy wklejasz lub zmieniasz kod w pliku w edytorze, pamiętaj o zapisaniu go później przy użyciu menu „...” lub klawisza skrótu (Ctrl+S w systemach Windows i Linux, Command+S w systemie macOS).

    W tym kodzie użyto platformy Mongoose w celu uproszczenia procesu przesyłania danych do i z usługi MongoDB. Mongoose to oparty na schemacie system do modelowania danych.

    Ten kod nawiązuje połączenie z bazą danych o nazwie „Books” na lokalnym serwerze usługi MongoDB. Następnie definiuje dokument bazy danych o nazwie „Book”, z podanym schematem. W schemacie zdefiniowano cztery pola, które opisują każdą książkę:

    • Nazwa, czyli tytuł książki.
    • Numer ISBN, czyli unikatowy identyfikator książki.
    • Autor.
    • Liczba stron.

    Następnie utworzysz programy obsługi HTTP mapujące żądania GET, POST i DELETE na operacje bazy danych.

Tworzenie tras platformy Express do obsługi żądań HTTP

  1. W edytorze otwórz plik app/routes.js i dodaj następujący kod.

    var path = require('path');
    var Book = require('./model');
    var routes = function(app) {
        app.get('/book', function(req, res) {
            Book.find({}, function(err, result) {
                if ( err ) throw err;
                res.json(result);
            });
        });
        app.post('/book', function(req, res) {
            var book = new Book( {
                name:req.body.name,
                isbn:req.body.isbn,
                author:req.body.author,
                pages:req.body.pages
            });
            book.save(function(err, result) {
                if ( err ) throw err;
                res.json( {
                    message:"Successfully added book",
                    book:result
                });
            });
        });
        app.delete("/book/:isbn", function(req, res) {
            Book.findOneAndRemove(req.query, function(err, result) {
                if ( err ) throw err;
                res.json( {
                    message: "Successfully deleted the book",
                    book: result
                });
            });
        });
        app.get('*', function(req, res) {
            res.sendFile(path.join(__dirname + '/public', 'index.html'));
        });
    };
    module.exports = routes;
    

    Ten kod tworzy cztery trasy dla aplikacji. Poniżej przedstawiono krótkie omówienie każdej z nich.

    Czasownik HTTP Punkt końcowy Opis
    GET /book Pobiera wszystkie książki z bazy danych.
    POST /book Tworzy obiekt Book na podstawie informacji wprowadzonych przez użytkownika w polach formularza internetowego i zapisuje ten obiekt w bazie danych.
    DELETE /book/:isbn Usuwa z bazy danych książkę o podanym numerze ISBN.
    GET * Zwraca stronę indeksu, jeśli nie dopasowano żadnej innej trasy.

    Platforma Express może dostarczać odpowiedzi HTTP bezpośrednio w ramach kodu obsługującego trasy lub dostarczać zawartość statyczną z plików. W tym kodzie przedstawiono obie możliwości. Pierwsze trzy trasy zwracają dane JSON w odpowiedzi na żądania dotyczące książek z interfejsu API. Czwarta trasa (przypadek domyślny) zwraca zawartość pliku indeksu index.html.

Tworzenie aplikacji klienckiej JavaScript

  1. W edytorze otwórz plik public/script.js i dodaj następujący kod:

    var app = angular.module('myApp', []);
    app.controller('myCtrl', function($scope, $http) {
        var getData = function() {
            return $http( {
                method: 'GET',
                url: '/book'
            }).then(function successCallback(response) {
                $scope.books = response.data;
            }, function errorCallback(response) {
                console.log('Error: ' + response);
            });
        };
        getData();
        $scope.del_book = function(book) {
            $http( {
                method: 'DELETE',
                url: '/book/:isbn',
                params: {'isbn': book.isbn}
            }).then(function successCallback(response) {
                console.log(response);
                return getData();
            }, function errorCallback(response) {
                console.log('Error: ' + response);
            });
        };
        $scope.add_book = function() {
            var body = '{ "name": "' + $scope.Name +
            '", "isbn": "' + $scope.Isbn +
            '", "author": "' + $scope.Author +
            '", "pages": "' + $scope.Pages + '" }';
            $http({
                method: 'POST',
                url: '/book',
                data: body
            }).then(function successCallback(response) {
                console.log(response);
                return getData();
            }, function errorCallback(response) {
                console.log('Error: ' + response);
            });
        };
    });
    

    Zwróć uwagę na zdefiniowany w kodzie moduł „myApp” i kontroler „myCtrl”. Nie zostaną tutaj omówione wszystkie szczegóły związane z działaniem modułów i kontrolerów, ale te nazwy będą używane w kolejnym kroku do powiązania interfejsu użytkownika (kodu HTML) z logiką biznesową aplikacji.

    Wcześniej utworzono cztery trasy do obsługi różnych operacji typu GET, POST i DELETE na serwerze. Ten kod dotyczy podobnych operacji, ale po stronie klienta (w przeglądarce internetowej użytkownika).

    Na przykład funkcja getData wysyła żądanie GET do punktu końcowego /book. Pamiętaj, że serwer obsługuje to żądanie, pobierając z bazy danych informacje o wszystkich książkach i zwracając je jako dane w formacie JSON. Zwróć uwagę, że wynikowe dane JSON są przypisane do zmiennej $scope.books. W kolejnym kroku zobaczysz, w jaki sposób wpływa to na informacje, które użytkownik zobaczy na stronie internetowej.

    Ten kod wywołuje funkcję getData podczas ładowania strony. Możesz zapoznać się z funkcjami del_book i add_book, aby poznać sposób ich działania. Nie jest potrzebny kod po stronie klienta odpowiadający domyślnej procedurze obsługi na serwerze, ponieważ ta domyślna procedura obsługi zwraca stronę indeksu, a nie dane JSON.

Tworzenie interfejsu użytkownika

  1. W edytorze otwórz plik public/index.html i dodaj następujący kod:

    <!doctype html>
    <html ng-app="myApp" ng-controller="myCtrl">
    <head>
        <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.7.2/angular.min.js"></script>
        <script src="script.js"></script>
    </head>
    <body>
        <div>
        <table>
            <tr>
            <td>Name:</td>
            <td><input type="text" ng-model="Name"></td>
            </tr>
            <tr>
            <td>Isbn:</td>
            <td><input type="text" ng-model="Isbn"></td>
            </tr>
            <tr>
            <td>Author:</td>
            <td><input type="text" ng-model="Author"></td>
            </tr>
            <tr>
            <td>Pages:</td>
            <td><input type="number" ng-model="Pages"></td>
            </tr>
        </table>
        <button ng-click="add_book()">Add</button>
        </div>
        <hr>
        <div>
        <table>
            <tr>
            <th>Name</th>
            <th>Isbn</th>
            <th>Author</th>
            <th>Pages</th>
            </tr>
            <tr ng-repeat="book in books">
            <td><input type="button" value="Delete" data-ng-click="del_book(book)"></td>
            <td>{{book.name}}</td>
            <td>{{book.isbn}}</td>
            <td>{{book.author}}</td>
            <td>{{book.pages}}</td>
            </tr>
        </table>
        </div>
    </body>
    </html>
    

    Ten kod tworzy podstawowy formularz HTML z czterema polami służący do przesyłania danych książek oraz tabelę zawierającą dane wszystkich książek, które są już przechowywane w bazie danych.

    Chociaż jest to standardowy kod HTML, atrybuty HTML ng- mogą wyglądać nieznajomo. Te atrybuty HTML łączą kod rozwiązania AngularJS z interfejsem użytkownika. Na przykład gdy użytkownik kliknie przycisk Dodaj, rozwiązanie AngularJS wywoła funkcję add_book, co spowoduje wysłanie danych z formularza na serwer.

    Zapoznaj się z kodem w tej sekcji, aby poznać powiązania poszczególnych atrybutów ng- z logiką biznesową aplikacji.

Tworzenie serwera platformy Express do hostowania aplikacji

  1. W edytorze otwórz plik server.js i dodaj następujący kod:

    var express = require('express');
    var bodyParser = require('body-parser');
    var app = express();
    app.use(express.static(__dirname + '/public'));
    app.use(bodyParser.json());
    require('./app/routes')(app);
    app.set('port', 80);
    app.listen(app.get('port'), function() {
        console.log('Server up: http://localhost:' + app.get('port'));
    });
    

    Ten kod służy do utworzenia aplikacji internetowej. Dostarcza ona pliki statyczne z katalogu public i obsługuje żądania za pomocą zdefiniowanych wcześniej tras.

Definiowanie informacji o pakietach oraz zależności

Jak możesz pamiętać, plik package.json zawiera informacje o aplikacji, w tym jej nazwę, opis oraz pakiety Node.js, których aplikacja potrzebuje do działania.

  1. W edytorze otwórz plik package.json i dodaj następujący kod:

    {
      "name": "books",
      "description": "Sample web app that manages book information.",
      "license": "MIT",
      "repository": {
        "type": "git",
        "url": "https://github.com/MicrosoftDocs/mslearn-build-a-web-app-with-mean-on-a-linux-vm"
      },
      "main": "server.js",
      "dependencies": {
        "express": "~4.16",
        "mongoose": "~5.3",
        "body-parser": "~1.18"
      }
    }
    

Zobaczysz informacje o aplikacji, czyli jej metadane, w tym jej nazwę, opis i licencję.

Pole repository określa lokalizację, w której przechowywany jest kod. Możesz później zapoznać się z tym kodem w witrynie GitHub, pod podanym tu adresem URL.

Pole main definiuje punkt wejścia aplikacji. Jest tutaj uwzględnione, aby pokazać kompletny kod, ale nie ma znaczenia, ponieważ nie zamierzasz publikować tej aplikacji jako pakietu Node.js do pobierania i używania przez inne osoby.

Pole dependencies jest ważne. Definiuje pakiety Node.js wymagane przez aplikację. Za chwilę ponownie połączysz się z maszyną wirtualną i uruchomisz polecenie npm install, aby zainstalować te pakiety.

Pakiety Node na ogół korzystają ze schematu wersjonowania semantycznego. Numer wersji składa się z trzech elementów: wersji głównej, wersji pomocniczej i numeru poprawki. Zapis z tyldą (~) wskazuje, że narzędzie npm powinno zainstalować najnowszą wersję poprawki w ramach wskazanej wersji głównej i pomocniczej. Podane tutaj wersje to najnowsze wersje, z którymi przetestowano ten moduł. W praktyce możesz z czasem zwiększać wersję, aktualizując i testując aplikację w celu korzystania z najnowszych funkcji poszczególnych pakietów zależności.

Kopiowanie plików na maszynę wirtualną

  1. Edycja plików została zakończona. Zamknij edytor, pamiętając o zapisaniu zmian we wszystkich plikach.

    Aby zamknąć edytor, kliknij wielokropek w rogu, a następnie wybierz polecenie Zamknij edytor.

  2. Uruchom następujące polecenie scp, aby skopiować zawartość katalogu ~/Books w sesji usługi Cloud Shell do katalogu o tej samej nazwie na maszynie wirtualnej.

    scp -r ~/Books azureuser@$ipaddress:~/Books
    

Instalowanie dodatkowych pakietów Node

Załóżmy, że podczas tworzenia aplikacji stwierdzisz, że potrzebujesz dodatkowych pakietów Node. Możesz na przykład pamiętać, że plik app/model.js zaczyna się od takiego wiersza.

var mongoose = require('mongoose');

Pamiętaj, że w aplikacji używana jest platforma Mongoose, ułatwiająca przesyłanie danych do i z bazy MongoDB.

Aplikacja wymaga także pakietów Express i body-parser. Pakiet body-parser to wtyczka umożliwiająca rozwiązaniu Express obsługę danych z formularza internetowego przesłanego przez klienta.

Nawiąż połączenie z maszyną wirtualną i zainstaluj pakiety określone w pliku package.json.

  1. Przed nawiązaniem połączenia z maszyną wirtualną upewnij się, że masz przygotowany jej adres IP. Jeśli nie, uruchom następujące polecenia w usłudze Cloud Shell, aby go pobrać.

    ipaddress=$(az vm show \
      --name MeanStack \
      --resource-group <rgn>[sandbox resource group name]</rgn> \
      --show-details \
      --query [publicIps] \
      --output tsv)
    
    echo $ipaddress
    
  2. Utwórz połączenie SSH z maszyną wirtualną, tak jak wcześniej.

    ssh azureuser@$ipaddress
    
  3. Przejdź do katalogu Books, który znajduje się w katalogu głównym.

    cd ~/Books
    
  4. Uruchom polecenie npm install, aby zainstalować pakiety zależne.

    npm install
    

Zachowaj otwarte połączenie SSH na potrzeby kolejnej części.

Testowanie aplikacji

Możesz teraz przystąpić do testowania aplikacji internetowej Node.js.

  1. Uruchom następujące polecenie z poziomu katalogu ~/Books, aby uruchomić aplikację internetową.

    sudo node server.js
    

    To polecenie uruchamia aplikację, nasłuchując przychodzących żądań HTTP na porcie 80.

  2. W nowej karcie przeglądarki przejdź do publicznego adresu IP maszyny wirtualnej.

    Zostanie wyświetlona strona indeksu, zawierająca formularz internetowy.

    Strona internetowa z formularzem i przyciskiem przesyłania.

    Spróbuj dodać kilka książek do bazy danych. Po dodaniu każdej książki pełna lista książek na stronie zostanie zaktualizowana.

    Możesz także kliknąć przycisk Delete (Usuń), aby usunąć książkę z bazy danych.