Toepassing met één pagina: KnockoutJS-sjabloon

De Knockout MVC-sjabloon maakt deel uit van ASP.NET en Web Tools 2012.2

Download ASP.NET en Web Tools 2012.2

De update ASP.NET en Web Tools 2012.2 bevat een Single-Page Application (SPA)-sjabloon voor ASP.NET MVC 4. Deze sjabloon is ontworpen om snel interactieve web-apps aan de clientzijde te bouwen.

"Single-page application" (SPA) is de algemene term voor een webtoepassing die een HTML-pagina laadt en vervolgens de pagina dynamisch bijwerkt in plaats van nieuwe pagina's te laden. Nadat de eerste pagina is geladen, communiceert de SPA met de server via AJAX-aanvragen.

Diagram met twee vakken met het label Client en Server. Een pijl met het label AJAX gaat van client naar server. Een pijl met het label H T M L en een pijl met het label J SON gaat van Server naar Client.

AJAX is niets nieuws, maar tegenwoordig zijn er JavaScript-frameworks waarmee u eenvoudiger een grote geavanceerde BEVEILIGD-WACHTWOORDVERIFICATIE-toepassing kunt bouwen en onderhouden. Bovendien maken HTML 5 en CSS3 het eenvoudiger om uitgebreide UIS's te maken.

Om aan de slag te gaan, maakt de SPA-sjabloon een voorbeeld van een "To-do list"-applicatie. In deze zelfstudie volgen we een rondleiding door de sjabloon. Eerst kijken we naar de takenlijsttoepassing zelf en onderzoeken we vervolgens de technologieonderdelen die ervoor zorgen dat deze werken.

Een nieuw SPA-sjabloonproject aanmaken

Vereisten:

  • Visual Studio 2012 of Visual Studio Express 2012 voor Web
  • ASP.NET Web Tools 2012.2 update. U kunt de update hier installeren.

Start Visual Studio en selecteer Nieuw project op de startpagina. Of selecteer in het menu Bestand de optie Nieuw en vervolgens Project.

Selecteer Geïnstalleerde sjablonen in het deelvenster Sjablonen en vouw het knooppunt Visual C# uit. Selecteer onder Visual C#Web. Selecteer ASP.NET MVC 4-webtoepassing in de lijst met projectsjablonen. Geef het project een naam en klik op OK.

Schermopname van het dialoogvenster Nieuw project. Een S P dot NET M V C 4-webtoepassing is geselecteerd in de lijst met websjablonen.

Selecteer toepassing met één pagina in de wizard Nieuw project.

Schermopname van het dialoogvenster New A S P dot NET M V C 4 Project. De toepassingssjabloon met één pagina is geselecteerd.

Druk op F5 om de toepassing te bouwen en uit te voeren. Wanneer de toepassing voor het eerst wordt uitgevoerd, wordt er een aanmeldingsscherm weergegeven.

Schermopname van het aanmeldingsscherm Mijn takenlijst.

Klik op de koppeling Registreren en maak een nieuwe gebruiker.

Schermopname van het aanmeldingsscherm.

Nadat u zich hebt aangemeld, maakt de toepassing een standaard to-do lijst met twee items. U kunt op Takenlijst toevoegen klikken om een nieuwe lijst toe te voegen.

Schermopname van twee takenlijsten en een knop Takenlijst toevoegen bovenaan.

Wijzig de naam van de lijst, voeg items toe aan de lijst en vink ze af. U kunt ook items verwijderen of een hele lijst verwijderen. De wijzigingen worden automatisch opgeslagen in een database op de server (eigenlijk LocalDB op dit moment, omdat u de toepassing lokaal uitvoert).

Schermopname van een lijst met drie items. Het laatste item is aangevinkt en doorgestreept.

Architectuur van de SPA-sjabloon

In dit diagram ziet u de belangrijkste bouwstenen voor de toepassing.

Diagram met de afzonderlijke bouwstenen van de client en server. Knockout dot j s, H T M L en J SON bevinden zich onder Client. Een S P DOT NET M V C, A S P P DOT NET Web A P I, Entity Framework en Database bevinden zich onder Server.

Aan de serverzijde dient ASP.NET MVC de HTML en verwerkt ook verificatie op basis van formulieren.

ASP.NET web-API verwerkt alle aanvragen die betrekking hebben op de ToDoLists en ToDoItems, waaronder ophalen, maken, bijwerken en verwijderen. De client wisselt gegevens uit met web-API in JSON-indeling.

Entity Framework (EF) is de O/RM-laag. Het mediaeert tussen de objectgerichte wereld van ASP.NET en de onderliggende database. De database maakt gebruik van LocalDB, maar u kunt dit wijzigen in het Web.config-bestand. Normaal gesproken gebruikt u LocalDB voor lokale ontwikkeling en implementeert u deze vervolgens in een SQL-database op de server, met behulp van EF-code-first-migratie.

Aan de clientzijde verwerkt de Knockout.js bibliotheek pagina-updates van AJAX-aanvragen. Knockout maakt gebruik van gegevensbinding om de pagina te synchroniseren met de meest recente gegevens. Op die manier hoeft u geen code te schrijven die de JSON-gegevens doorloopt en de DOM bijwerken. In plaats daarvan plaatst u declaratieve kenmerken in de HTML die Knockout vertellen hoe de gegevens moeten worden weergegeven.

Een groot voordeel van deze architectuur is dat de presentatielaag wordt gescheiden van de toepassingslogica. U kunt het web-API-gedeelte maken zonder iets te weten over hoe uw webpagina eruitziet. Aan de clientzijde maakt u een 'weergavemodel' om die gegevens weer te geven en het weergavemodel maakt gebruik van Knockout om verbinding te maken met de HTML. Hiermee kunt u de HTML eenvoudig wijzigen zonder het weergavemodel te wijzigen. (We kijken een beetje later naar Knockout.)

Modellen

In het Visual Studio-project bevat de map Modellen de modellen die aan de serverzijde worden gebruikt. (Er zijn ook modellen aan de clientzijde; daar komen we bij.)

Schermopname van de map Modellen geopend.

TodoItem, TodoList

Dit zijn de databasemodellen voor Entity Framework Code First. U ziet dat deze modellen eigenschappen hebben die naar elkaar verwijzen. ToDoList bevat een verzameling ToDoItems en elk ToDoItem bevat een verwijzing naar de bovenliggende ToDoList. Deze eigenschappen worden navigatie-eigenschappen genoemd, en ze vertegenwoordigen de een-op-veel-relatie tussen een to-do lijst en de bijbehorende to-do items.

De ToDoItem klasse gebruikt ook het kenmerk [ForeignKey] om op te geven dat ToDoListId een refererende sleutel in de ToDoList tabel is. Dit vertelt EF om een foreign-key beperking toe te voegen aan de database.

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

TodoItemDto, TodoListDto

Deze klassen definiëren de gegevens die naar de client worden verzonden. 'DTO' staat voor 'data transfer object'. De DTO definieert hoe de entiteiten worden geserialiseerd in JSON. Over het algemeen zijn er verschillende redenen om DTO's te gebruiken.

  • Bepalen welke eigenschappen worden geserialiseerd. De DTO kan een subset van de eigenschappen van het domeinmodel bevatten. U kunt dit doen om veiligheidsredenen (gevoelige gegevens verbergen) of gewoon om de hoeveelheid gegevens te verminderen die u verzendt.
  • Als u de vorm van de gegevens wilt wijzigen, bijvoorbeeld om een complexere gegevensstructuur af te vlakken.
  • Om bedrijfslogica buiten de DTO te houden (scheiding van problemen).
  • Als uw domeinmodellen om een of andere reden niet kunnen worden geserialiseerd. Kringverwijzingen kunnen bijvoorbeeld problemen veroorzaken wanneer u een object serialiseert. Er zijn manieren om dit probleem op te lossen in de web-API (zie Afhandeling van verwijzingen naar kringvormige objecten); maar het gebruik van een DTO voorkomt het probleem helemaal.

In de SPA-sjabloon bevatten de DTO's dezelfde gegevens als de domeinmodellen. Ze zijn echter nog steeds nuttig omdat ze kringverwijzingen van de navigatie-eigenschappen vermijden en het algemene DTO-patroon demonstreren.

AccountModels.cs

Dit bestand bevat modellen voor sitelidmaatschap. De UserProfile klasse definieert het schema voor gebruikersprofielen in de lidmaatschapsdatabase. (In dit geval is de enige informatie de gebruikers-id en de gebruikersnaam.) De andere modelklassen in dit bestand worden gebruikt om de gebruikersregistratie- en aanmeldingsformulieren te maken.

Entity Framework

De SPA-sjabloon maakt gebruik van EF Code First. In Code First-ontwikkeling definieert u eerst de modellen in code en vervolgens gebruikt EF het model om de database te maken. U kunt EF ook gebruiken met een bestaande database (Database First).

De TodoItemContext klasse in de map Modellen is afgeleid van DbContext. Deze klasse biedt de 'lijm' tussen de modellen en EF. Het TodoItemContext bevat een ToDoItem verzameling en een TodoList verzameling. Als u een query wilt uitvoeren op de database, schrijft u gewoon een LINQ-query voor deze verzamelingen. Hier ziet u bijvoorbeeld hoe u alle to-do lijsten voor gebruiker Alice kunt selecteren:

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

U kunt ook nieuwe items toevoegen aan de verzameling, items bijwerken of items uit de verzameling verwijderen en de wijzigingen in de database behouden.

ASP.NET Web-API-controllers

In ASP.NET Web-API zijn controllers objecten die HTTP-aanvragen verwerken. Zoals vermeld, maakt de SPA-sjabloon gebruik van de Web-API om CRUD-bewerkingen op ToDoList- en ToDoItem-exemplaren mogelijk te maken. De controllers bevinden zich in de map Controllers van de oplossing.

Schermopname waarin de map Controllers is geopend. De bestanden Controller.cs en ToDoListController.cs zijn beide rood omcirkeld.

  • TodoController: VERWERKT HTTP-aanvragen voor to-do items
  • TodoListController: verwerkt HTTP-aanvragen voor to-do lijsten.

Deze namen zijn belangrijk, omdat de Web API het URI-pad koppelt aan de naam van de controller. (Zie Routering in ASP.NET Web-API voor meer informatie over het routeren van HTTP-aanvragen naar controllers.)

Laten we eens kijken naar de ToDoListController klasse. Het bevat één gegevenslid:

private TodoItemContext db = new TodoItemContext();

De TodoItemContext wordt gebruikt om te communiceren met EF, zoals eerder beschreven. De methoden op de controller implementeren de CRUD-bewerkingen. Web-API wijst HTTP-aanvragen van de client als volgt toe aan controllermethoden:

HTTP-aanvraag Controllermethode Beschrijving
GET /api/todo GetTodoLists Verkrijgt een verzameling to-do lijsten.
GET /api/todo/id GetTodoList Haalt een takenlijst op aan de hand van ID
PUT /api/todo/id PutTodoList Hiermee wordt een to-do lijst bijgewerkt.
POST /api/todo PostTodoList Hiermee maakt u een nieuwe to-do lijst.
DELETE /api/todo/id DeleteTodoList Hiermee verwijdert u een takenlijst.

U ziet dat de URI's voor sommige bewerkingen tijdelijke aanduidingen voor de id-waarde bevatten. Als u bijvoorbeeld een takenlijst met een id van 42 wilt verwijderen, is de URI /api/todo/42.

Zie Een web-API maken die CRUD-bewerkingen ondersteunt voor meer informatie over het gebruik van web-API's voor CRUD-bewerkingen. De code voor deze controller is vrij eenvoudig. Hier volgen enkele interessante punten:

  • De GetTodoLists methode gebruikt een LINQ-query om de resultaten te filteren op de id van de aangemelde gebruiker. Op die manier ziet een gebruiker alleen de gegevens die bij hem of haar horen. U ziet ook dat een Select-instructie wordt gebruikt om de ToDoList exemplaren te converteren naar TodoListDto exemplaren.
  • De PUT- en POST-methoden controleren de modelstatus voordat u de database wijzigt. Als ModelState.IsValid onwaar is, retourneren deze methoden HTTP 400, Ongeldige aanvraag. Lees meer over modelvalidatie in web-API bij modelvalidatie.
  • De controllerklasse is ook ingericht met het kenmerk [Autoriseren]. Met dit kenmerk wordt gecontroleerd of de HTTP-aanvraag is geverifieerd. Als de aanvraag niet is geverifieerd, ontvangt de client HTTP 401, niet geautoriseerd. Lees meer over verificatie bij Verificatie en autorisatie in ASP.NET Web-API.

De TodoController klasse is vergelijkbaar met TodoListController. Het grootste verschil is dat er geen GET-methoden worden gedefinieerd, omdat de client de to-do items samen met elke to-do lijst krijgt.

MVC-controllers en -weergaven

De MVC-controllers bevinden zich ook in de map Controllers van de oplossing. HomeController geeft de hoofd-HTML voor de toepassing weer. De weergave voor de startcontroller is gedefinieerd in Views/Home/Index.cshtml. De startweergave geeft verschillende inhoud weer, afhankelijk van of de gebruiker is aangemeld:

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

Wanneer gebruikers zijn aangemeld, zien ze de hoofdgebruikersinterface. Anders zien ze het aanmeldingsvenster. Houd er rekening mee dat deze voorwaardelijke rendering aan de serverzijde plaatsvindt. Probeer nooit gevoelige inhoud aan de clientzijde te verbergen. Alles wat u in een HTTP-antwoord verzendt, is zichtbaar voor iemand die de onbewerkte HTTP-berichten bekijkt.

Client-Side JavaScript en Knockout.js

Nu gaan we van de serverzijde van de toepassing naar de client. Het SPA-sjabloon maakt gebruik van een combinatie van jQuery en Knockout.js om een soepele, interactieve gebruikersinterface te creëren. Knockout.js is een JavaScript-bibliotheek waarmee u HTML eenvoudig aan gegevens kunt koppelen. Knockout.js gebruikt een patroon met de naam Model-View-ViewModel.

  • Het model is de domeingegevens (ToDo-lijsten en ToDo-items).
  • De weergave is het HTML-document.
  • Het weergavemodel is een JavaScript-object dat de modelgegevens bevat. Het weergavemodel is een codeabstractie van de gebruikersinterface. Er is geen kennis van de HTML-weergave. In plaats daarvan vertegenwoordigt het abstracte kenmerken van de weergave, zoals 'een lijst met Taken-items'.

De weergave is gegevens gebonden aan het weergavemodel. Updates voor het weergavemodel worden automatisch weergegeven in de weergave. Bindingen werken ook in de andere richting. Gebeurtenissen in de DOM (zoals klikken) zijn gegevensgebonden aan functies in het weergavemodel, waarmee AJAX-aanroepen worden geactiveerd.

De SPA-sjabloon organiseert de JavaScript aan de clientzijde in drie lagen.

  • todo.datacontext.js: VERZENDT AJAX-aanvragen.
  • todo.model.js: definieert de modellen.
  • todo.viewmodel.js: definieert het weergavemodel.

Diagram met een pijl van Knockout.js naar Weergave model naar Modellen naar gegevenscontext. De pijl tussen Knockout.js en Weergave model heeft het label Gegevensbinding en verwijst naar beide elementen.

Deze scriptbestanden bevinden zich in de map Scripts/app van de oplossing.

Schermopname van de geopende submap met het label app.

todo.datacontext verwerkt alle AJAX-aanroepen naar de Web-API-controllers. (De AJAX-aanroepen voor aanmelden worden elders gedefinieerd, in ajaxlogin.js.)

todo.model.js definieert de clientmodellen (browser) voor de to-do-lijsten. Er zijn twee modelklassen: todoItem en todoList.

Veel van de eigenschappen in de modelklassen zijn van het type 'ko.observable'. Observables zijn de manier waarop Knockout zijn magie werkt. Uit de Knockout-documentatie: Een waarneembaar is een 'JavaScript-object waarmee abonnees kunnen worden geïnformeerd over wijzigingen'. Wanneer de waarde van een waarneembare wijziging wordt doorgevoerd, worden alle HTML-elementen die aan deze waarneembare elementen zijn gebonden, bijgewerkt. TodoItem heeft bijvoorbeeld waarneembare waarden voor de titel en isDone-eigenschappen:

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

U kunt zich ook abonneren op een observable in code. De todoItem-klasse abonneert zich bijvoorbeeld op wijzigingen in de eigenschappen 'isDone' en 'title':

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

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

Model weergeven

Het weergavemodel wordt gedefinieerd in todo.viewmodel.js. Het weergavemodel is het centrale punt waar de toepassing de HTML-pagina-elementen verbindt met de domeingegevens. In de SPA-sjabloon bevat het weergavemodel een waarneembare array met todoLists. De volgende code in het weergavemodel vertelt Knockout om de bindingen toe te passen:

ko.applyBindings(window.todoApp.todoListViewModel);

HTML- en gegevensbinding

De hoofd-HTML voor de pagina is gedefinieerd in Views/Home/Index.cshtml. Omdat we gegevensbinding gebruiken, is de HTML alleen een sjabloon voor wat er daadwerkelijk wordt weergegeven. Knockout maakt gebruik van declaratieve bindingen. U koppelt pagina-elementen aan gegevens door een kenmerk 'data-bind' toe te voegen aan het element. Hier volgt een heel eenvoudig voorbeeld uit de Knockout-documentatie:

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

In dit voorbeeld werkt Knockout de inhoud van het <span-element> bij met de waarde van myItems.count(). Wanneer deze waarde wordt gewijzigd, wordt het document bijgewerkt door Knockout.

Knockout biedt een aantal verschillende bindingstypen. Hier volgen enkele bindingen die worden gebruikt in de SPA-sjabloon:

  • foreach: Hiermee kunt u een lus herhalen en dezelfde markering toepassen op elk item in de lijst. Dit wordt gebruikt om de to-do lijsten en to-do items weer te geven. Binnen de foreach worden de bindingen toegepast op de elementen van de lijst.
  • zichtbaar: wordt gebruikt om zichtbaarheid in te schakelen. Verberg markeringen wanneer een verzameling leeg is of maak het foutbericht zichtbaar.
  • waarde: Wordt gebruikt om formulierwaarden in te vullen.
  • klik: Koppelt een klik-gebeurtenis aan een functie in het weergavemodel.

Anti-CSRF-beveiliging

CSRF (Cross-Site Request Forgery) is een aanval waarbij een schadelijke site een aanvraag verzendt naar een kwetsbare site waar de gebruiker momenteel is aangemeld. Om CSRF-aanvallen te helpen voorkomen, gebruikt ASP.NET MVC antivervalsingstokens, ook wel verificatietokens voor aanvragen genoemd. Het idee is dat de server een willekeurig gegenereerd token op een webpagina plaatst. Wanneer de client gegevens naar de server verzendt, moet deze waarde in het aanvraagbericht worden opgenomen.

Antivervalsingstokens werken omdat de schadelijke pagina de tokens van de gebruiker niet kan lezen vanwege beleid voor dezelfde oorsprong. (Beleid voor dezelfde oorsprong voorkomt dat documenten die worden gehost op twee verschillende sites, toegang hebben tot elkaars inhoud.)

ASP.NET MVC biedt ingebouwde ondersteuning voor antivervalsingstokens via de antiforgery-klasse en het kenmerk [ValidateAntiForgeryToken]. Deze functionaliteit is momenteel niet ingebouwd in web-API. De SPA-sjabloon bevat echter een aangepaste implementatie voor de Web-API. Deze code wordt gedefinieerd in de ValidateHttpAntiForgeryTokenAttribute klasse, die zich in de map Filters van de oplossing bevindt. Zie CsrF-aanvallen (Cross-Site Request Forgery) voorkomen voor meer informatie over anti-CSRF in web-API.

Conclusion

De SPA-sjabloon is ontworpen om u snel op weg te helpen om moderne, interactieve webtoepassingen te schrijven. Het maakt gebruik van de Knockout.js-bibliotheek om de presentatie (HTML-opmaak) te scheiden van de gegevens- en toepassingslogica. Knockout is niet de enige JavaScript-bibliotheek die u kunt gebruiken om een Single Page Application (SPA) te maken. Als u enkele andere opties wilt verkennen, bekijkt u de door de gemeenschap gemaakte SPA-sjablonen.