Aplikacja jednostronicowa: szablon KnockoutJS

Szablon Knockout MVC jest częścią narzędzi ASP.NET i Web Tools 2012.2

Pobierz narzędzia ASP.NET i Web Tools 2012.2

Aktualizacja ASP.NET i Web Tools 2012.2 zawiera szablon "Single-Page Application" (SPA) dla ASP.NET MVC 4. Ten szablon został zaprojektowany w celu szybkiego rozpoczęcia tworzenia interaktywnych aplikacji internetowych po stronie klienta.

"Aplikacja jednostronicowa" (SPA) to ogólny termin dla aplikacji internetowej, która ładuje pojedynczą stronę HTML, a następnie aktualizuje stronę dynamicznie, zamiast ładować nowe strony. Po załadowaniu początkowej strony SPA rozmawia z serwerem za pośrednictwem żądań AJAX.

Diagram przedstawiający dwa pola z etykietą Klient i Serwer. Strzałka oznaczona etykietą AJAX przechodzi z klienta na serwer. Strzałka oznaczona etykietą H T M L i strzałka oznaczona etykietą J SON przechodzi z serwera do klienta.

AJAX nie jest niczym nowym, ale obecnie istnieją struktury JavaScript, które ułatwiają tworzenie i konserwację dużej wyrafinowanej aplikacji SPA. Ponadto html 5 i CSS3 ułatwiają tworzenie rozbudowanych interfejsów użytkownika.

Aby rozpocząć, szablon SPA tworzy przykładową aplikację "Lista zadań do wykonania". W tym samouczku przedstawimy przewodnik po szablonie. Najpierw przyjrzymy się samej aplikacji listy zadań do wykonania, a następnie zbadamy elementy technologiczne, które sprawiają, że działa.

Tworzenie nowego projektu szablonu SPA

Wymagania:

  • Visual Studio 2012 lub Visual Studio Express 2012 for Web
  • aktualizacja ASP.NET Web Tools 2012.2. Możesz zainstalować aktualizację tutaj.

Uruchom program Visual Studio i wybierz pozycję Nowy projekt na stronie Start. Lub w menu Plik wybierz pozycję Nowy , a następnie pozycję Projekt.

W okienku Szablony wybierz pozycję Zainstalowane szablony i rozwiń węzeł Visual C# . W obszarze Visual C# wybierz pozycję Sieć Web. Na liście szablonów projektów wybierz pozycję ASP.NET aplikacja internetowa MVC 4. Nadaj projektowi nazwę i kliknij przycisk OK.

Zrzut ekranu przedstawiający okno dialogowe Nowy projekt. Z listy szablonów sieci Web wybrano aplikację internetową S P dot NET M V C 4.

W kreatorze Nowy projekt wybierz pozycję Aplikacja jednostronicowa.

Zrzut ekranu pokazujący okno dialogowe Nowy projekt ASP.NET MVC 4. Wybrano szablon Aplikacja jednostronicowa.

Naciśnij klawisz F5, aby skompilować i uruchomić aplikację. Po pierwszym uruchomieniu aplikacji zostanie wyświetlony ekran logowania.

Zrzut ekranu przedstawiający ekran logowania Moja lista zadań.

Kliknij link "Zarejestruj się" i utwórz nowego użytkownika.

Zrzut ekranu przedstawiający ekran Rejestracja.

Po zalogowaniu aplikacja tworzy domyślną listę zadań do wykonania z dwoma elementami. Możesz kliknąć pozycję "Dodaj listę zadań do wykonania", aby dodać nową listę.

Zrzut ekranu przedstawiający dwie listy zadań do wykonania i przycisk Dodaj listę zadań u góry.

Zmień nazwę listy, dodaj elementy do listy i zaznacz je. Możesz również usunąć elementy lub usunąć całą listę. Zmiany są automatycznie utrwalane w bazie danych na serwerze (w rzeczywistości localDB w tym momencie, ponieważ aplikacja jest uruchomiona lokalnie).

Zrzut ekranu przedstawiający listę z trzema elementami. Ostatni element jest zaznaczony i przekreślony.

Architektura szablonu SPA

Na tym diagramie przedstawiono główne bloki konstrukcyjne aplikacji.

Diagram przedstawiający oddzielne bloki konstrukcyjne Klienta i Serwera. Knockout.js, HTML i JSON są w obszarze Klienta. ASP.NET MVC, ASP.NET Web API, Entity Framework i baza danych znajdują się w obszarze Serwera.

Po stronie serwera ASP.NET MVC obsługuje kod HTML, a także obsługuje uwierzytelnianie oparte na formularzach.

ASP.NET Web API obsługuje wszystkie żądania powiązane z ToDoLists i ToDoItems, w tym pobieranie, tworzenie, aktualizowanie i usuwanie. Klient wymienia dane za pomocą internetowego interfejsu API w formacie JSON.

Platforma Entity Framework (EF) to warstwa O/RM. Pośredniczy między obiektowym światem ASP.NET a bazową bazą danych. Baza danych używa bazy danych LocalDB, ale można to zmienić w pliku Web.config. Zazwyczaj do programowania lokalnego należy używać bazy danych LocalDB, a następnie wdrażać je w bazie danych SQL na serwerze przy użyciu migracji najpierw kodu ef.

Po stronie klienta biblioteka Knockout.js obsługuje aktualizacje stron z żądań AJAX. Funkcja Knockout używa powiązania danych do synchronizowania strony z najnowszymi danymi. W ten sposób nie trzeba pisać żadnego kodu, który przetwarza dane JSON i aktualizuje DOM. Zamiast tego należy umieścić atrybuty deklaratywne w kodzie HTML, które informują Knockout, jak prezentować dane.

Zaletą tej architektury jest oddzielenie warstwy prezentacji od logiki aplikacji. Możesz utworzyć część internetowego interfejsu API bez znajomości sposobu wyglądu strony internetowej. Po stronie klienta utworzysz "model wyświetlania", aby reprezentować te dane, a model widoku używa platformy Knockout do powiązania z kodem HTML. Umożliwia to łatwą zmianę kodu HTML bez zmiany modelu widoku. (Przyjrzymy się Knockout nieco później).

Models

W projekcie programu Visual Studio folder Models zawiera modele używane po stronie serwera. (Istnieją również modele po stronie klienta; przejdziemy do nich).

Zrzut ekranu przedstawiający otwarty folder Models.

TodoItem, TodoList

Są to modele bazy danych dla programu Entity Framework Code First. Zwróć uwagę, że te modele mają właściwości wskazujące siebie nawzajem. ToDoList zawiera kolekcję elementów ToDoItems, a każdy z nich ToDoItem zawiera odwołanie do nadrzędnej listy ToDoList. Te właściwości są nazywane właściwościami nawigacyjnymi i reprezentują relację jeden do wielu między listą to-do a jej elementami to-do.

Klasa ToDoItem używa również atrybutu [ForeignKey], aby określić, że ToDoListId jest to klucz obcy w ToDoList tabeli. Dzięki temu program EF doda ograniczenie klucza obcego do bazy danych.

[ForeignKey("TodoList")]
public int TodoListId { get; set; }
public virtual TodoList TodoList { get; set; }

TodoItemDto, TodoListDto

Te klasy definiują dane, które będą wysyłane do klienta. "DTO" oznacza "obiekt transferu danych". Obiekt DTO definiuje sposób serializacji jednostek w formacie JSON. Ogólnie rzecz biorąc, istnieje kilka powodów, dla których należy użyć obiektów transferu danych (DTO):

  • Aby kontrolować, które właściwości są serializowane. DTO może zawierać podzestaw właściwości z modelu domeny. Możesz to zrobić ze względów bezpieczeństwa (aby ukryć poufne dane) lub po prostu zmniejszyć ilość wysyłanych danych.
  • Aby zmienić kształt danych , np. spłaszczyć bardziej złożoną strukturę danych.
  • Aby zachować dowolną logikę biznesową poza DTO (separacja problemów).
  • Jeśli z jakiegoś powodu nie można serializować modeli domeny. Na przykład odwołania cykliczne mogą powodować problemy podczas serializacji obiektu Istnieją sposoby obsługi tego problemu w internetowym interfejsie API (zobacz Obsługa odwołań do obiektów cyklicznych); ale użycie DTO po prostu unika problemu całkowicie.

W szablonie SPA obiekty DTO zawierają te same dane co modele domeny. Są one jednak nadal przydatne, ponieważ unikają odwołań cyklicznych z właściwości nawigacji i przedstawiają ogólny wzorzec DTO.

AccountModels.cs

Ten plik zawiera modele członkostwa w witrynie. Klasa UserProfile definiuje schemat profilów użytkowników w bazie danych członkostwa. (W tym przypadku jedynymi informacjami są identyfikator użytkownika i nazwa użytkownika). Inne klasy modeli w tym pliku służą do tworzenia formularzy rejestracji i logowania użytkownika.

Entity Framework

Szablon SPA używa EF Code First. W programowania Code First należy najpierw zdefiniować modele w kodzie, a następnie program EF używa modelu do utworzenia bazy danych. Możesz również użyć programu EF z istniejącą bazą danych (Database First).

Klasa TodoItemContext w folderze Models pochodzi z elementu DbContext. Ta klasa stanowi "klej" między modelami a EF. Obiekt TodoItemContext przechowuje kolekcję ToDoItem oraz TodoList. Aby wykonać zapytanie dotyczące bazy danych, wystarczy napisać zapytanie LINQ względem tych kolekcji. Na przykład poniżej przedstawiono sposób wybierania wszystkich list to-do dla użytkownika "Alice":

TodoItemContext db = new TodoItemContext();
IEnumerable<TodoList> lists = 
    from td in db.TodoLists where td.UserId == "Alice" select td;

Możesz również dodawać nowe elementy do kolekcji, aktualizować elementy lub usuwać elementy z kolekcji i utrwalać zmiany w bazie danych.

kontrolery internetowego interfejsu API ASP.NET

W ASP.NET internetowym interfejsie API kontrolery są obiektami obsługującymi żądania HTTP. Jak wspomniano, szablon SPA używa internetowego interfejsu API, aby umożliwić operacje CRUD na instancjach ToDoList i ToDoItem. Kontrolery znajdują się w folderze o nazwie "Controllers" w rozwiązaniu.

Zrzut ekranu pokazujący otwarty folder Kontrolery. Pliki

  • TodoController: obsługuje żądania HTTP dla elementów listy zadań
  • TodoListController: obsługuje żądania HTTP dla listy zadań.

Te nazwy są istotne, ponieważ interfejs API sieci Web pasuje ścieżkę URI ze nazwą kontrolera. (Aby dowiedzieć się, jak internetowy interfejs API kieruje żądania HTTP do kontrolerów, zobacz Routing w interfejsie API sieci Web ASP.NET).

Przyjrzyjmy się klasie ToDoListController. Zawiera ono jedną składową danych:

private TodoItemContext db = new TodoItemContext();

Element TodoItemContext służy do komunikowania się z EF zgodnie z wcześniejszym opisem. Metody na kontrolerze implementują operacje CRUD. Internetowy interfejs API mapuje żądania HTTP od klienta na metody kontrolera w następujący sposób:

Żądanie HTTP Metoda kontrolera Opis
GET /api/todo GetTodoLists Pobiera kolekcję list zadań.
GET /api/todo/id GetTodoList Pobiera listę zadań według identyfikatora
PUT /api/todo/id PutTodoList Aktualizuje listę zadań.
POST /api/todo PostTodoList Tworzy nową listę to-do.
DELETE /api/todo/id DeleteTodoList Usuwa listę ZADAŃ DO WYKONANIA.

Zwróć uwagę, że niektóre operacje mają URI z miejscami zastępczymi na wartość identyfikatora. Aby na przykład usunąć to-listę z identyfikatorem 42, identyfikator URI to /api/todo/42.

Aby dowiedzieć się więcej na temat korzystania z internetowego interfejsu API dla operacji CRUD, zobacz Tworzenie internetowego interfejsu API obsługującego operacje CRUD. Kod tego kontrolera jest dość prosty. Oto kilka interesujących kwestii:

  • Metoda GetTodoLists używa zapytania LINQ do filtrowania wyników według identyfikatora zalogowanego użytkownika. W ten sposób użytkownik widzi tylko dane, które należą do niego. Zwróć również uwagę, że instrukcja Select jest używana do konwersji wystąpień ToDoList na wystąpienia TodoListDto.
  • Metody PUT i POST sprawdzają stan modelu przed zmodyfikowaniem bazy danych. Jeśli parametr ModelState.IsValid ma wartość false, te metody zwracają http 400, nieprawidłowe żądanie. Przeczytaj więcej na temat walidacji modelu w internetowym interfejsie API na stronie Walidacja modelu.
  • Klasa kontrolera jest również ozdobiona atrybutem [Autoryzuj]. Ten atrybut sprawdza, czy żądanie HTTP jest uwierzytelnione. Jeśli żądanie nie zostanie uwierzytelnione, klient otrzyma błąd HTTP 401 , Brak autoryzacji. Przeczytaj więcej na temat uwierzytelniania na stronie Uwierzytelnianie i autoryzacja w interfejsie API sieci Web ASP.NET.

Klasa TodoController jest bardzo podobna do klasy TodoListController. Największą różnicą jest to, że nie definiuje żadnych metod GET, ponieważ klient otrzyma to-do itemy razem z każdą listą zadań.

Kontrolery i widoki MVC

Kontrolery MVC znajdują się również w folderze Controllers rozwiązania. HomeController Renderuje główny kod HTML dla aplikacji. Widok kontrolera home jest zdefiniowany w pliku Views/Home/Index.cshtml. Widok Narzędzia główne renderuje różną zawartość w zależności od tego, czy użytkownik jest zalogowany:

@if (@User.Identity.IsAuthenticated)
{
    // ....
}

Gdy użytkownicy są zalogowani, widzą główny interfejs użytkownika. W przeciwnym razie widzą panel logowania. Należy pamiętać, że to renderowanie warunkowe odbywa się po stronie serwera. Nigdy nie próbuj ukrywać poufnej zawartości po stronie klienta — wszystko, co wysyłasz w odpowiedzi HTTP, jest widoczne dla osoby, która obserwuje nieprzetworzone komunikaty HTTP.

Client-Side JavaScript i Knockout.js

Teraz przejdźmy od strony serwera aplikacji do klienta. Szablon SPA używa kombinacji jQuery i Knockout.js do utworzenia płynnego, interaktywnego interfejsu użytkownika. Knockout.js to biblioteka języka JavaScript, która ułatwia powiązanie kodu HTML z danymi. Knockout.js używa wzorca o nazwie "Model-View-ViewModel".

  • Model to dane domeny (listy zadań do wykonania i elementy zadań do wykonania).
  • Widok jest dokumentem HTML.
  • Model widoku jest obiektem JavaScript, który przechowuje dane modelu. Model widoków to abstrakcja kodu interfejsu użytkownika. Nie ma wiedzy na temat reprezentacji HTML. Zamiast tego reprezentuje abstrakcyjne funkcje widoku, takie jak "lista elementów zadań do wykonania".

Widok jest powiązany z danymi w modelu widoku. Aktualizacje modelu widoku są automatycznie odzwierciedlane w widoku. Powiązania działają także w innym kierunku. Zdarzenia w modelu DOM (takie jak kliknięcia) są powiązane z danymi do funkcji w modelu widoku, które wyzwalają wywołania AJAX.

Szablon SPA organizuje kod JavaScript po stronie klienta na trzy warstwy:

  • todo.datacontext.js: wysyła żądania AJAX.
  • todo.model.js: definiuje modele.
  • todo.viewmodel.js: definiuje model widoku.

Diagram przedstawiający strzałkę biegnącą od Knockout dot j s do View Model, Models i Data Context. Strzałka między Knockout dot j s a View Model jest oznaczona etykietą

Te pliki skryptów znajdują się w folderze Scripts/app w ramach rozwiązania.

Zrzut ekranu przedstawiający otwarty podfolder z etykietą aplikacja.

todo.datacontext obsługuje wszystkie wywołania AJAX do kontrolerów internetowego interfejsu API. (Wywołania AJAX do logowania są definiowane gdzie indziej w ajaxlogin.js.)

todo.model.js definiuje modele po stronie klienta (przeglądarki) dla list to-do. Istnieją dwie klasy modeli: todoItem i todoList.

Wiele właściwości w klasach modelu ma typ "ko.observable". Obserwowalne są jak Knockout robi swoją magię. W dokumentacji Knockout stwierdzono, że obiekt obserwowalny to "obiekt JavaScript, który może powiadamiać subskrybentów o zmianach". Gdy wartość obiektu obserwowalnego się zmienia, Knockout aktualizuje wszystkie elementy HTML powiązane z obiektami obserwowalnymi. Na przykład todoItem ma obserwowalne właściwości dla tytułu i isDone:

self.title = ko.observable(data.title);
self.isDone = ko.observable(data.isDone);

Możesz również subskrybować możliwe do obserwowania w kodzie. Na przykład klasa todoItem subskrybuje zmiany we właściwościach "isDone" i "title":

saveChanges = function () {
    return datacontext.saveChangedTodoItem(self);
};

// Auto-save when these properties change
self.isDone.subscribe(saveChanges);
self.title.subscribe(saveChanges);

Wyświetl model

Model widoku jest zdefiniowany w todo.viewmodel.js. Model widoku jest centralnym punktem, w którym aplikacja wiąże elementy strony HTML z danymi domeny. W szablonie SPA model widoku zawiera zauważalną tablicę todoLists. Poniższy kod w modelu widoku informuje Knockout o zastosowaniu powiązań:

ko.applyBindings(window.todoApp.todoListViewModel);

Powiązanie HTML i danych

Główny kod HTML strony jest zdefiniowany w pliku Views/Home/Index.cshtml. Ponieważ używamy powiązania danych, kod HTML jest tylko szablonem dla tego, co faktycznie jest renderowane. Funkcja Knockout używa powiązań deklaratywnych . Elementy strony są powiązane z danymi przez dodanie atrybutu "data-bind" do elementu. Przykład: Bardzo prosty przykład pobrany z dokumentacji Knockout:

<p>There are <span data-bind="text: myItems().count"></span> items<p>

W tym przykładzie aplikacja Knockout aktualizuje zawartość <elementu span> o wartości myItems.count(). Za każdym razem, gdy ta wartość ulegnie zmianie, aplikacja Knockout aktualizuje dokument.

Knockout udostępnia wiele różnych typów powiązań. Oto niektóre powiązania używane w szablonie SPA:

  • foreach: umożliwia iterowanie przez pętlę i stosowanie tego samego znacznika do każdego elementu na liście. Służy do renderowania list zadań i pozycji na liście zadań. W foreach powiązania są stosowane do elementów listy.
  • visible: służy do przełączania widoczności. Ukryj znaczniki, gdy kolekcja jest pusta lub wyświetl komunikat o błędzie.
  • wartość: służy do wypełniania wartości formularza.
  • kliknięcie: tworzy powiązanie zdarzenia kliknięcia z funkcją w modelu widoku.

Ochrona przed csrf

Fałszowanie żądań międzywitrynowych (CSRF) to atak, w którym złośliwa witryna wysyła żądanie do podatnej na ataki witryny, w której użytkownik jest obecnie zalogowany. Aby zapobiec atakom CSRF, ASP.NET MVC używa tokenów przeciw fałszerzowaniu, nazywanych również tokenami weryfikacji żądań. Chodzi o to, że serwer umieszcza losowo wygenerowany token na stronie internetowej. Gdy klient przesyła dane do serwera, musi uwzględnić tę wartość w komunikacie żądania.

Tokeny ochrony przed fałszerzowaniem działają, ponieważ złośliwa strona nie może odczytać tokenów użytkownika ze względu na zasady tego samego źródła. (Zasady tego samego źródła uniemożliwiają dostęp do zawartości dokumentów hostowanych w dwóch różnych witrynach).

ASP.NET MVC zapewnia wbudowaną obsługę tokenów ochrony przed fałszerzami za pośrednictwem klasy AntiForgery i atrybutu [ValidateAntiForgeryToken]. Obecnie ta funkcja nie jest wbudowana w internetowy interfejs API. Jednak szablon SPA zawiera niestandardową implementację internetowego interfejsu API. Ten kod jest zdefiniowany w ValidateHttpAntiForgeryTokenAttribute klasie, która znajduje się w folderze Filtry rozwiązania. Aby dowiedzieć się więcej o ochronie przed CSRF w Web API, zobacz Zapobieganie atakom fałszerstwa żądań między witrynami (CSRF).

Podsumowanie

Szablon SPA został zaprojektowany w celu szybkiego rozpoczęcia pisania nowoczesnych, interaktywnych aplikacji internetowych. Używa biblioteki Knockout.js do oddzielenia prezentacji (znaczników HTML) od danych i logiki aplikacji. Ale Knockout nie jest jedyną biblioteką JavaScript, której można użyć do utworzenia SPA. Jeśli chcesz zapoznać się z innymi opcjami, zapoznaj się z utworzonymi przez społeczność szablonami SPA.