V tomto článku bych vám rád představil novou technologii od Microsoftu, která se nazývá ASP.NET MVC. Ke stažení je v rámci ASP.NET 3.5 Extensions Preview odsud http://www.asp.net/downloads/3.5-extensions/. Nebudeme psát žádný kód, ale spíše se zaměříme na rozbor jednotlivých vlastností a schopností tohoto frameworku.
Hlavními výhodami ASP.NET MVC je smysluplné mapování URL na akce naší aplikace, snadná manipulovatelnost s HTML kódem, čistý designový návrh celého kódu a v neposlední řadě modularita celého frameworku, čili jednoduchá náhrada jednotlivých částí. Toto všechno si postupně vysvětlíme, takže nevěšte hlavu, pokud vám teď některý z pojmů není jasný.
ASP.NET MVC není náhradou klasického ASP.NET. Tyto dvě metodiky budou nadále koexistovat, budou obě rovnocenně podporovány a tvrdím si říci, že i rovnocenně využívány. Tak jako některým programátorům nesedí vývoj v současném ASP.NET, pro jakýsi pocit nedostatečné kontroly nad aplikací (sám se mezi ně počítám), tak také nehrozí přechod všech vývojářů k MVC, pro jeho jakýsi návrat ke kořenům a nutnost se mnohem více zabývat html, css či celkovým zprovozněním dané funkcionality.
V současné době je MVC pouze ve verzi Comunity Technical Preview a tudíž může obsahovat bugy a v žádném případě není jeho funkční množina konečná.
Model View Controller
Středobodem celého frameworku (a koneckonců odtud pochází i název) je návrhový vzor (design pattern) Model View Controller.
MVC je sám o sobě starý již skoro třicet let. Proč je kolem něj tedy takový humbuk až nyní? Jednoduše proto, že je velmi vhodný pro beze-stavé aplikace jež webové stránky představují. Tuto „beze-stavovost“ řeší klasické ASP.NET pomocí mnohými proklínaného post-backu, jenž do aplikace jakýsi stav dodává, který je možné udržet mezi jednotlivými načteními stránky. Čili post-back de facto simuluje – alespoň z pohledu developera – desktop aplikace. Kdežto MVC neváže stav aplikace na zobrazenou stránku, ale uchovává ho v datové vrstvě, případně v controllerech.
Ale web tu máme také již nějaký ten pátek, stále tedy nemáme vysvětlení, proč je MVC tak populární až nyní? Odpovědí je Ruby on Rails. Ano tento framework pro vývoj webových aplikací se stal v posledních několika letech velmi rozšířený a „vytáhl“ s sebou na výsluní i MVC.
A co že to tedy MVC vůbec je, ptáte se? Inu zřetelné oddělení business logiky, designu a samotných dat od sebe samých. Konkrétně:
· Model – zdroj dat, ať již SQL databáze, XML, text či cokoliv jiného.
· View – neboli „pohled“, uživatelské rozhraní, část starající se o zobrazení výstupu naší aplikace (a ničeho jiného!).
· Controller - ten se stará o business logiku, změny nad daty, obstarání událostí atd.
Další výhodou MVC návrhového vzoru je snadná implementace unit testingu. Je možné testovat jednotlivé akce a controllery, neboť je jasný jak vstupní tak výstupní stav při volání akcí. A díky podpoře unit testingu jak ve frameworku tak v novém Visual Studiu se stává jeho používání velmi příjemnou prací.
URL mapping
URL mapping je termín označující mapování URL cest (požadavků) na konkrétní controller, který požadavek obslouží. Hlavní devizou je možnost mapovat smysluplné URL na funkcionalitu naší stránky. Z toho důvodu je URL mapping také vhodný třeba pro implementaci REST požadavků a jde ruku v ruce se SEO optimalizacemi stránky.
Defaultně je v ASP.NET MVC mapping nastaven takto:
RouteTable.Routes.Add(new Route
{
Url = "[controller]/[action]/[id]",
Defaults = new { action = "Index", id = (string)null },
RouteHandler = typeof(MvcRouteHandler)
});
A co nám to říká? Pro ilustraci si představme online produktový katalog, který umožňuje zobrazovat seznam produktů, zobrazovat produkty podle kategorií či zobrazovat podrobnosti o produktech. Níže uvádím různé možnosti, jaké by naše aplikace mohla obsluhovat:
|
.../Home/Index
|
Controller: HomeController
Action: Index
Id: null
|
Úvodní stránka celého katalogu.
Zavolá se akce Index controlleru Home. Každý controller může mít akci „Index“ vlastní…
|
|
…/Home/
|
Controller: HomeController
Action: Index
Id: null
|
Stejné jak výše, neboť jsme nastavili „Index“ jako defaultní akci v případě, že není žádná specifikována v našem požadavku
|
|
…/Products/View
|
Controller: ProductsController
Action: View
Id: null
|
Zavolá se akce View controlleru Products bez specifikovaného Id. Naše aplikace by v tomto případě mohla třeba vypsat seznam všech produktů.
|
|
/Products/View/1
|
Controller: ProductsController
Action: View
Id: 1
|
Zavolá se akce View controlleru Products s Id 1. Naše aplikace by pak zobrazila detail produktu s Id 1.
|
URL mapping je 100% konfigurovatelný, není problém nastavit mapping dle jakýchkoliv našich požadavků či zákazníkových přání. Mohli bychom tak například prohodit [action] a [id] a na naše příspěvky se dotazovat /Products/1/View. Popřípadě vůbec nemusíme používat Id, ale kupříkladu vhodnější název produktu - pak bychom používali třeba /Products/ondruv-super-produkt/View, datum vytvoření produktu atd.
Data Model
Jak již bylo řečeno dříve, náš zdroj dat může být cokoliv – SQL, XML, prostý text… Avšak v nejvíce případech se bude jednat právě o SQL databázi a díky technologii LINQ (konkrétně LINQ to SQL) a novému Visual Studiu 2008 to také bude případ nejpohodlnější. Díky LINQu je možné nechat si automaticky vygenerovat .NET třídy reprezentující naší relační databázi a pak k nim pohodlně přistupovat v aplikaci s plnou podporou Intellisense, typovou bezpečností a zachovanými relačními údaji (cizí klíče atd.). Podporovány jsou také alternativní poskytovatelé relačních dat, jako například NHibernate.
Takto vypadá návrh datového modelu ve Visual Studiu 2008:
Controller&Actions
Controller se stará o samotný běh aplikace, její business logiku, obsluhu událostí atd. Pro nás jako programátory to znamená naimplementovat pro každý controller třídy, která bude potomkem MVC třídy Controller. Tato bázová třída nám umožňuje přistupovat k objektům reprezentujícím http požadavek a odpověď, k objektu představujícímu uživatele v rámci aplikace, umožňuje nám volat vykreslování Views, přesměrovávání požadavků na jiné akce a mnoho dalšího.
Akce jsou implementovány jako public funkce jednotlivých controllerů, které mají speciální příznak [ControllerAction] (poznámka: Atribut [ControllerAction] již nebude nutné uvádět v nové verzi ASP.NET MVC.). Akce jsou potom volány ASP.NET enginem dle nastaveného mapování a představují logicky oddělené celky. Kdybychom tak například implementovali CRUD metodiku (Create, Read, Update, Delete) pro naše data, každá z těchto metod bude jednou akcí. Stejně tak, když budeme implementovat produktový katalog, vytvoříme si nějaký Controller ProductsController a v něm akce View pro zobrazení detailu produktu, Update pro jeho upravení a třeba Buy pro zakoupení…
V další verzi MVC frameworku (který bude uvolněn na konferenci MIX08) již také bude podpora pro tzv. filtrování akcí. To znamená, že bude možné pomocí atributu funkce nastavit chování před a po zavolání akce či controlleru. Konkrétní příklad je třeba zamezení přístupu k dané akci pro uživatele, kteří nejsou přihlášení.
Ukázkový kód, kostra produktového controlleru.
public class ProductsController : Controller
{
//
//Sample URL: Products/View/1
// Products/View/5
//
[ControllerAction]
public void View(int id)
{
//get product data from the database and pass it along
//to the view
RenderView("View");
}
//
//Sample URL: Products/List/
//
[ControllerAction]
public void List()
{
//get all the products from the database and display it
RenderView("List");
}
}
View
View část celé MVC metodiky je ta část, která se stará o prezentaci výsledků uživateli. Standardní způsob představuje .aspx stránka s vloženými bloky kódu (tedy trochu něco jako klasické ASP). Já se pak níže ještě zmíním o jiném view enginu, který mi přijde o dost elegantnější a který vám bez debaty ušetří mnoho psaní.
MVC podporuje jak klasické .aspx stránky, tak master stránky známé z ASP.NET 2.0. Co však použít můžeme pouze v omezené míře jsou ASP.NET controlly. Důvodem je chybějící post-back a vlastně celý koncept příznaku runat=“server“. Jako do jisté míry náhrada controllů byl představen koncept tzv. html helperů. Což jsou funkce, které se zadanými parametry generují příslušný html kód. Takovéto helpery můžeme použít pro generování správných linků na naše controllery/akce, pro generování html formulářů či pro vkládání obrázků. V současné verzi ASP.NET MVC jsou helper funkce k dispozici jako externí download (http://go.microsoft.com/fwlink/?LinkID=106001&clcid=0x409), ale již ve verzi nové (k dispozici po konferenci MIX08) budou integrovány přímo do frameworku.
A nyní již ukázkový kód, jak by vypadala stránka, vypisující všechny produkty nějaké kategorie v našem smyšleném katalogu:
<%@ Page Language="C#" AutoEventWireup="true"
CodeBehind="List.aspx" Inherits=" List" Title="Products" %>
<asp:Content ID="Content1" ContentPlaceHolderID="ph" runat="server">
<h2><%= ViewData.CategoryName %></h2>
<ul>
<% foreach (var product in ViewData.Products) { %>
<li>
<%= product.ProductName %>
<div class="editlink">
(<%= Html.ActionLink("Edit", new { Action="Edit",
ID=product.ProductID })%>)
</div>
</li>
<% } %>
</ul>
<%= Html.ActionLink("Add New Product", new { Action="New" }) %>
</asp:Content>
NHaml
Jak jsem slíbil, zde popíšu alternativní view engine založený na jazyku Haml jehož implementace v .NET se nazývá NHaml. Vše, co je potřeba udělat k podpoře NHaml ve vaší aplikaci je referencovat patřičnou knihovnu a pak NHaml zaregistrovat jako engine starající se o views, což je jednořádková záležitost. Abych vám dokázal jeho jednoduchost a eleganci, uvádím výše popsaný view kód pro výpis produktů přepsaný do NHaml.
%h2= ViewData.CategoryName
%ul
- foreach (var product in ViewData.Products)
%li
= product.ProductName
.editlink
= Html.ActionLink("Edit", new { Action="Edit", ID=product.ProductID })
= Html.ActionLink("Add New Product", new { Action="New" })
No není to o poznání jednodušší? Toto je, prosím, celý kód, ne pouze relevantní úryvek či část řešení potřebného pro provozování NHaml. Toto je celý view kód. Na této ukázce je vidět několik aspektů celého jazyka.
1. „%“ - uvádí xhtml tagy
2. „-“ – kód, který přímo nevrací výsledek referovaný ve stránce
3. „=“ – kód, který vrací výsledek, který se stane součástí xhml
4. „.“ – název třídy xhtml elementu
5. „#“ – id xhtml elementu
6. …
Výše uvedený výčet se jen dotýká povrchu všech možností jazyka Haml. Ten totiž nabízí mnoho dalších a řekl bych i zajímavějších „vychytávek“. A proto, pokud vás (N)Haml zaujal, doporučuji obrátit svou pozornost sem http://andrewpeters.net/2007/12/19/introducing-nhaml-an-aspnet-mvc-view-engine/
Předávání dat z Controlleru do View a zpět
Esenciální záležitostí při psaní MVC aplikace, je předávání dat do view k vykreslení a zpět, čili čtení dat s uživatelem vyplněných formulářů. K tomuto účelu obsahuje každý view property ViewData, kde jsou uložená data předávaná z controlleru do view. ViewData můžou být i typovány na nějaký konkrétní typ, neb bázový view je generics a jeho typ určuje právě typ ViewData. Pak se tedy můžeme na naše data odkazovat bez dodatečného přetypování a tudíž bezpečně.
Při získáni dat zpět od uživatele funguje ASP.NET MVC tak, že jednotlivé vstupní pole formulářů jsou podle svého jména namapovány na vstupní parametry volané akce. Pokud budu mít tedy formulář, který se bude ptát na uživatelův věk a bude obsahovat vstupní pole na zadání právě tohoto věku (jméno textového pole UserAge) a já budu formulář posílat na stránku/akci se jménem SubmitUserAge, pak UserAge také bude parametr funkce SubmitUserAge na příslušném kontroleru.
K tomuto přístupu existuje ještě alternativní, obzvláště vhodný při rozsáhlejších formulářích. Jeho princip spočívá v tom, že akci controlleru bereme jako bezparametrickou a v jejím těle zavoláme rozšiřující metodu (nová funkcionalita c# 3.0) UpdateFrom, která opět naplní libovolnou datovou strukturu z formulářových dat podle jména.
Závěr
Každá technologie, ať už je sebedokonalejší, má i svá negativa. V našem případě jsou zápory dány hlavně díky značnému mládí celé technologie a jejímu nevyzrání. Jedná se v první řadě o nativně nepodporovaný AJAX a ASP.Net controlly. Avšak musíme mít na paměti, že ASP.NET MVC je v současné době pouze Community Technical Preview, a že Microsoft opravdu naslouchá členům vývojářské komunity. Takže pokud se vám něco nebude líbít, nebo budete chtít do frameworku prosadit nějakou vlastnost, stačí být dostatečně hlasitý (a nechtít blbosti J ).
I přes tyto nedostatky byl framework přijat vývojářskou komunitou s nadšením a to hlavně těmi developery, kterým nesedí současný přístup k vývoji webových aplikací v ASP.NET a kteří mají radši vetší kontrolu nad svými aplikacemi. Stejně tak nativní podpora pro SEO, smysluplné mapování URL adres na funkcionalitu aplikace, čistý design a podpora unit testingu činí z ASP.NET MVC atraktivní technologii a všichni zajisté budeme s napětím sledovat její další rozvoj.
Ondřej Šťastný
Developer, Microsoft Student Partner
ondrej.stastny@memos.cz