Ćwiczenie — tworzenie podstawowej aplikacji internetowej

Ukończone

Na tym etapie na maszynie wirtualnej z systemem Ubuntu jest zainstalowana 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. Aplikacja internetowa umożliwia wyświetlanie listy informacji o książkach, dodawanie nowych książek i usuwanie istniejących 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.

Screenshot of a web page with a form and submission button.

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

  • Bazy danych MongoDB przechowuje informacje o książkach.
  • Platforma Express.js kieruje każde żądanie HTTP do odpowiedniej procedury 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. 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. Co z Express.js, E w akronimie MEAN?

Express.js to struktura serwera internetowego utworzona 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 składa się ze ścieżki lub identyfikatora URI oraz metody żądania, takiej jak 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. W dalszej części tej lekcji utworzysz plik o nazwie package.json definiującej platformę Express i inne zależności, a następnie uruchom 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.

Usługa AngularJS ułatwia pisanie i testowanie aplikacji internetowych, ponieważ umożliwia lepsze oddzielenie wyglądu strony internetowej — kodu HTML — od sposobu zachowania strony internetowej. 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. W tym miejscu załadujesz usługę AngularJS z sieci dostarczania zawartości lub usługi 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. Zazwyczaj ten kod jest dodany do <head> sekcji 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 (Secure Copy Protocol). Następnie uruchomisz aplikację Node.js i zobaczysz wyniki w przeglądarce.

W praktyce zazwyczaj napiszesz i przetestujesz aplikację internetową w bardziej lokalnym środowisku, na przykład z laptopa lub z maszyny wirtualnej uruchamianej lokalnie. Następnie możesz przechowywać kod w systemie kontroli wersji, takim jak Git, i używać ciągłej integracji i ciągłego dostarczania — lub ciągłego wdrażania — systemu, takiego jak Usługa Azure DevOps, w celu przetestowania zmian i przekazania ich do maszyny wirtualnej. Na końcu tego modułu znajdziesz linki do dodatkowych zasobów.

Tworzenie aplikacji internetowej z informacjami o książkach

W tym miejscu utworzysz cały kod, skrypt i pliki HTML tworzące 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 to 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ące elementy:

    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. Kod 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.
    • Jego numer międzynarodowej standardowej książki (lub ISBN), który jednoznacznie identyfikuje książkę
    • Autor.
    • Liczba stron.

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

Tworzenie tras Express.js obsługujących żądania HTTP

  1. W edytorze otwórz app/routes.js plik 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.

    Plik Express.js może obsługiwać odpowiedzi HTTP bezpośrednio w kodzie obsługującym trasy lub obsługiwać 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. Dowiesz się, jak wpływa to na to, co użytkownik widzi na stronie internetowej w następnym kroku.

    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. Do dopasowania domyślnego programu obsługi serwera nie jest potrzebny kod po stronie klienta, ponieważ 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 do przesyłania danych książki i tabelą, w której są wyświetlane wszystkie książki 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 po wybraniu przycisku Dodaj, rozwiązanie AngularJS wywoła funkcję add_book, co spowoduje wysłanie danych z formularza na serwer.

    W tym miejscu możesz sprawdzić kod, aby dowiedzieć się, jak każdy z atrybutów ng- odnosi się do logiki biznesowej aplikacji.

Tworzenie serwera Express.js 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. Obsługuje pliki statyczne z public katalogu i używa tras zdefiniowanych wcześniej do obsługi żądań.

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 on udostępniany w tym miejscu do ukończenia, ale nie jest to ważne, ponieważ nie planujesz publikowania aplikacji jako pakietu Node.js dla innych osób do pobrania i użycia.

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. Wyświetlone 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ą

Przed kontynuowaniem upewnij się, że adres IP maszyny wirtualnej jest przydatny. Jeśli go nie masz, uruchom następujące polecenia z usługi Cloud Shell, aby je pobrać:

ipaddress=$(az vm show \
  --name MeanStack \
  --resource-group "<rgn>[sandbox resource group name]</rgn>" \
  --show-details \
  --query [publicIps] \
  --output tsv)
echo $ipaddress
  1. Edycja plików została zakończona. Upewnij się, że zapisano zmiany w każdym pliku, a następnie zamknij edytor.

    Aby zamknąć edytor, wybierz wielokropek w prawym górnym rogu, a następnie wybierz pozycję Zamknij edytor.

  2. Uruchom następujące scp polecenie, aby skopiować zawartość ~/Books katalogu w sesji usługi Cloud Shell do tej samej nazwy katalogu 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 również pakietów Express.js i body-parser. Body-parser to wtyczka umożliwiająca platformie Express pracę z danymi z formularza internetowego wysył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 go nie masz, uruchom polecenia usługi Cloud Shell w poprzedniej sekcji, aby je pobrać.

  2. Podobnie jak wcześniej, utwórz połączenie SSH z maszyną wirtualną:

    ssh azureuser@$ipaddress
    
  3. Przejdź do Books katalogu w katalogu głównym:

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

    sudo apt install npm -y && npm install
    

Pozostaw otwarte połączenie SSH dla następnej sekcji.

Testowanie aplikacji

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

  1. ~/Books W katalogu uruchom następujące polecenie, aby uruchomić aplikację internetową:

    sudo nodejs 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.

    Screenshot of the book web page with a form and submission button.

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

    Screenshot of the book web page with sample data populated.

    Aby usunąć książkę z bazy danych, możesz również wybrać pozycję Usuń.