Klávesové zkratky na tomto webu - rozšířené Na obsah stránky

Lokalizované URL v ASP.NET MVC

11.26 - 24. ledna 2010 | ASP.NET 2.0

Pokud se dostanete do situace, že budete potřebovat lokalizovat URL adresy vaší webové aplikace, máte hned několik možností, jak to řešit.

Jedno z možných řešení, které se mi ani trochu nelíbí, popsal Augi ve svém článku ASP.NET MVC – lokalizace URL. Já mám raději jednoduchá řešení, která nepotřebují hackovat systém. :)

Když jsme začali psát Trop, byl celý v angličtině. Díky konvencím, které jsem popsal v článku Pojmenované routy v ASP.NET MVC, měla naprostá většina stránek svojí vlastní routu. A protože definice routy není nic jiného než textový řetězec, přesunul jsem jej do resource souboru Routes.resx. To byl první krok.

Když jsme vymýšleli, jakým způsobem poběží jednotlivé jazykové mutace, bylo jasné, že každá mutace bude mít vlastní top-level doménu a bude to samostatná instance aplikace. To je super, protože se pak dá využít konfigurační sekce globalization, kde se nastaví jazyk prostředí konkrétní instance, podle kterého se i vybírají konkrétní resource soubory.

Česká instance pak má ve web.configu následující řádek:

<globalization culture="cs-CZ" />

Díky tomu se použijí routovací pravidla z Routes.cs.resx a URL adresy se generují česky, aniž bych musel v kódu cokoli změnit. Samozřejmě nejsou přeložená všechna routovací pravidla, spousta akcí, které jsou pouze POST, překlad nepotřebuje, protože nejsou součástí prezentační vrstvy.

Nakonec veškeré úsilí, které bylo k implementaci lokalizovaných URL potřeba, bylo v tom, embedovat definované routy do resource souboru, s čímž mi ochotně pomohl Refactor! Pro. :) Zbytek je věc konfigurace v deployovacím scriptu. Ale to už je na jiné povídání.

Ošetření chybových stavů v ASP.NET MVC

12.50 - 22. prosince 2009 | ASP.NET 2.0

Každá aplikace se může dostat do stavu, kdy není vše, jak má být, a nastane chyba. Tyto stavy bychom neměli ignorovat, ale pečlivě ošetřovat. Jak na to se podíváme v tomto spotu.

ASP.NET obsahuje poměrně sofistikovaný mechanismus na zpracování chyb. Ukazuje celkem podrobné chybové výpisy, které jsou nám vývojářům velice užitečné. Nicméně nejsou užitečné naším uživatelům a ještě mohou leccos prozradit našim útočníkům. Proto by v produkci měly být tyto hlášky zakázané.

Konfigurace

Každá aplikace v produkci by měla mít nastavené pěkné chybové stránky, bez podrobností, co se stalo, ale s informacemi, kam dál uživatel může pokračovat. Popřípadě mu pomoci nalézt to, co hledal. K tomu slouží konfigurační element customErrors, kde můžeme nastavit režim zobrazování a jednotlivé stránky, které se mají zobrazit na určitý chybový kód.

Bohužel, systém je navržen zcela kokotsky a pokud chcete v konfiguraci nastavit pro 404 a 500 jiné stránky, jsou vraceny s kódem 200. Proto využijeme jen následující konfiguraci. Zbylá je celkem k ničemu.

<customErrors mode="RemoteOnly" />

Tím docílíme toho, že všechny requesty, které nejsou z tohoto serveru obdrží pěkné chybové stránky. Dotaz z daného serveru dostane podrobný chybový výpis, což se může hodit.

Zpracování chyb

ASP.NET MVC má v sobě základní mechanismus pro ošetřování chyb. Je jím ActionFilter HandleError, který renderuje pohled Error.aspx, pokud dojde k probublání neošetřené výjimky až do akce řadiče.

[HandleError]
public class MyController : Controller {
}

Procento chyb, takto zachytitelných, jistě není malé, ovšem není 100%. Nemusíme chodit pro příklad daleko a stačí udělat chybku v šabloně pohledu. Hned tu máme nepěknou chybovou stránku.

Takovouto chybu odchytíme až v Global.asax:

protected void Application_Error() {
#if DEBUG
  return;
#endif

  Exception exception = Server.GetLastError();

  Response.Clear();

  RouteData routeData = new RouteData();
  routeData.Values.Add("controller", "Error");

  if (IsPageNotFoundException(exception as HttpException)) {
    routeData.Values.Add("action", "NotFound");
  }
  else {
    routeData.Values.Add("action", "Error");
  }

  Server.ClearError();

  IController errorController = new ErrorController();
  errorController.Execute(new RequestContext(new HttpContextWrapper(Context), routeData));
}

Pokud máme aplikaci v debug modu (typicky u vývojáře) je vhodné nechat chyby probublat až k němu. Pokud jsme ale v releasu musíme chybu zpracovat. Na výpis chybové stránky nám poslouží ErrorController, který má dvě akce: NotFound pro 404 a Error pro 500.

public class ErrorController : Controller {

  [StatusCode(HttpStatusCode.NotFound)]
  public ActionResult NotFound() {
    return View();
  }

  [StatusCode(HttpStatusCode.InternalServerError)]
  public ActionResult Error() {
    return View();
  }
}

Díky způsobu hledání šablon v ASPX View enginu, můžeme na Error využít stejnou Error.aspx ve složce Shared jako pro chyby zachycené už na řadiči.

Neplatná referenční identita

Teď se ještě dostaneme k tomu, proč tu máme zpracování HttpException s kódem 404. Kde se vezme?

Typicky, když máme nějakou detail page, snažíme se pomocí nějakého jedinečného identifikátoru natáhnout zobrazovanou entitu. Pokud žádnou nenajdeme je vhodné vyhodit výjimku a protože jsme na úrovni webové aplikace, měla by to být HttpException s kódem 404, která říká, že hledaná stránka nebyla nalezena.

Její zpracování už máme výše vyřešeno. A to je snad pro dnešek vše. :)

Mikroformáty jako datový model stránky

12.47 - 18. listopadu 2009 | Webdesign

Dříve, když jsem psal webové aplikace, kde bylo potřeba hodně JavaScriptu, aby se dosáhlo pohodlné práce, duplikoval jsem data. Ano, je to tak a stydím se za to. Většina dat, které se na stránce vypisovaly, byla duplicitní. Jednou byly v HTML a podruhé v JSON, aby se s nimi dalo snadno pracovat v JavaScriptu. Jenže všichni víme, že duplikování je zlo. :)

Tropu jsem se tomuto nešvaru snažil vyhnout. A tak je většina dat ve stránkách označena mikroformáty. Výhodou mikroformátů je, že jsou snadno strojově zpracovatené. Tudíž i JavaScriptem. Pokud se třeba podíváte na detail místa (např. The Pub na Staroměstské) uvidíte mapku, kde je bublina s kontaktními údaji místa. Tato data nejsou v žádném JSON ve stránce, ani se nestahují AJAXovým dotazem. Script jednoduše vezme data z mikroformátu hCard včetně geo souřadnic, kde se má bublina vykreslit.

Podobný model je aplikován na různých místech Tropu.

Mikroformáty vs. RDFa

Myslím, že v tomto případě se i ukazuje, že mikroformáty jsou lepší než RDFa. Protože parsovat mikroformáty pomocí jQuery je mnohem snažší a přehlednější. RDFa je možná flexibilnější v tom, kolik gramatik dokáže pojmout, ale to je i jeho zásadní slabinou. Mikroformáty definují jednoduché formáty, které staví na jednoduchém modelu HTML. Existují nástroje, díky nimž lze s nimi snadno pracovat.

Mikroformáty a ASP.NET MVC

Úžasnou novinkou ASP.NET MVC 2 bude extenze Html.DisplayFor, kde se vytvoří jediná šablona třeba s vizitkou uživatele a pak jí mužu používat všude, aniž bych někde na něco zapomněl nebo něco nabořil. Dnes musím mít několik extenzí pro různé typy dat, kde mám navíc kód přímo v extenzi. Sice je jednoduchý, ale čím méně HTML v C# kódu, tím lépe. (On to tedy není přímo HTML kód, ale jeho objektová reprezentace.)

Práce s mikroformáty tak bude mnohem jednodušší a ucelenější. :)

Community meeting .net

12.50 - 7. října 2009 | ASP.NET 2.0

Máte nějaké dotazy ohledně vývoje v ASP.NET potažmo v .net obecně? Máte chuť se seznámit s dalšími lidmi, kteří se dotnetem zabývají, nebo jen chcete osobně potkat lidi, které znáte jen virtuálně? Budete v sobotu 17. Října na WebExpu 2009?

Pokud jste alespoň na jednu z výše položených otázek odpověděli kladně, přijďte se podívat na Community meeting .net, kde najdete Štěpána Bechynského a moji maličkost. Můžete se nás zeptat na cokoli o ASP.NET a dalších dotnet technologiích a pokud to bude v našich silách, tak vám odpovíme. Štěpán slíbil, že součástí meetingu bude i coolkeg s náplní od Staropramenu, tak kdo bude mít žížu, tak ať si přinese vlastní pulitr, pokud nechce pít z plastiku…

Naviděnou příští sobotu!

Tip na přehlednější šablony

09.25 - 12. července 2009 | ASP.NET 2.0

Zastávám názor, že šablona by měla být co nejvíce přehledná a obsahovat co nejméně programového kódu. Pojďme se podívat na některé kousky zasmrádlého kódu a jak se s nimi vypořádat.

Html.ActionLink je pro mne jedním z prvních míst, kde zvednout ukazováček a říct: „takhle ne.“ Již ve spotu o routingu jsem psal, že procházení routovací tabulky při hledání akce je výpočetně náročné a pokud vaše tabulka obsahuje mnoho řádků, může být zdrojem nepříjemného zdržení. Mějme například helper odkaz na detail zboží v jehož URL obsahuje informace o kategorii zboží, jeho id a název:

<%= Html.ActionLink(Model.Name, "Detail", "Goods", new { id = Model.Id, name = Model.UrlName, category = Model.Category.UrlName }) %>

Tohle rozhodně není přehledné, ani výkonné. Začněme tedy refaktorovat. Nelíbí se mi, že tag odkazu je generovaný a že pořádně netuším, jestli třeba nejsou parametry ve špatném pořadí. A co potom takový kodér? Co když bude chtít přidat nějakou třídu, aby mohl tento konkrétní odkaz lépe nastylovat nebo mu dát nějaký sémantický význam? Přejděme na Url.Action!

<a href="<%= Url.Action("Detail", "Goods", new { id = Model.Id, name = Model.UrlName, category = Model.Category.UrlName }) %>"><%= Html.Encode(Model.Name) %></a>

No moc jsme si nepomohli. Sice teď máme větší kontrolu nad generovaným kódem, ale kód je o dost delší a pořád pěkně „smrdí.“ Zbavme se teď kouzelných řetězců, ty mohou být zdrojem špatně dohledatelných chyb!

<a href="<%= Url.RouteUrl(RouteTo.CommodityDetail, new { id = Model.Id, name = Model.UrlName, category = Model.Category.UrlName }) %>"><%= Html.Encode(Model.Name) %></a>

Zbavili jsme se možnosti udělat chybu v názvu akce nebo řadiče a navíc teď probíhá vyhledávání routy v tabulce podle klíče, tudíž mnohem efektivněji. Ale pořád mi tu něco smrdí. Ano je to ta anonymní třída, i tady je velká šance na vnesení chyby. Ačkoli to tak na první pohled nevypadá, názvy vlastností této třídy nejsou nic jiného než kouzelné řetězce, jen bez uvozovek. Na pozadí se totiž překládají jako klíče ve slovníku. Lék na to je jednoduchý. Vytvoříme si vlastní helper!

<a href="<%= Url.CommodityDetail(Model) %>"><%= Html.Encode(Model.Name) %></a>

Zápis se rázem zpřehlednil, snížila se možnost zanesení chyby a množství opakovaného kódu.

Validace v ASP.NET MVC

13.56 - 2. května 2009 | ASP.NET 2.0

Bezpodmínečnou nutností každé aplikace, která zpracovává data z neznámých zdrojů, je validace vstupních dat. V ASP.NET je poměrně sofistikovaná sada komponent, které přidají k vašim vstupním polím validační podmínky a v případě jejich nesplnění je uživatel informován. Validace probíhá jak na straně klienta (pomocí JavaScriptu), tak i na straně serveru. Jenže tyto komponenty jsou plně závislé na serverovém formuláři a v ASP.NET MVC je tedy nelze použít.

Nyní mírně odbočím. Z předchozího odstavce jste si mohli odnést jednu myšlenku. A to, že validace je součástí UI vrstvy aplikace. Pokud však píšete aplikaci, která obsahu spoustu business pravidel, není to úplně nejšťastnější, když máte validační pravidla v UI a pak ještě někde ve vašem modelu. Jedno ze základních pravidel dobrého návrhu říká DRY (don’t repeat yourself – neopakuj se).

ModelState

ASP.NET MVC tedy nepřináší validátory, ale infrastrukturu, která umožňuje v případě chyby uživatele upozornit. Tato infrastruktura je postavená na stavu modelu (ModelState). Stav modelu má vlastnost IsValid a metodu AddModelError. V HtmlHelperu pak najdete dvě extenze ValidationMessage a ValidationSummary, které vypisují chybové hlášky pro konkrétní pole nebo sumář pro všechny. A to je vše, co nám MVC poskytuje. Další práce už je jen na nás.

Jak pracovat se stavem modelu na nejnižší úrovni ukazuje jeden z tutoriálů: Performing Simple Validation. Možná vám to bude stačit, ale pro mne je to příliš práce navíc a ani trochu mi to nepřijde sexy. :) Jaké máme další možnosti?

Můžeme využít vlastní validační vrstvu naší business logiky a napojit jí na stav modelu, tak, jak to ukazuje následující tutoriál: Validating with a Service Layer. Nebo vyžijeme existujících validačních frameworků. Dnes se podíváme na System.Componen­tModel.DataAn­notations a příště na Caste.Componen­ts.Validators.

DataAnnotations

Anotace dat je novinka z ASP.NET Dynamic Data, která usnadňuje automatické generování ASP.NET validátorů na základě modelu. Tyto anotace se dále rozšiřují a v .net 4.0 by to měla být hodně použitelná věcička, která bude v ASP.NET DD, ASP.NET MVC a dokonce i v .net RIA Services. Každopádně nemusíme čekat na velký release platformy a využít anotací již dnes. Jak to tedy funguje?

Jediné, co musíte udělat, je označit vlastnosti vašeho modelu atributy:

[Required]
public string Name { get; set; }

[RegularExpression("^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$ ")]
public string Email { get; set; }

Dalším krokem je zaregistrovat nový model binder ` DataAnnotation­sModelBinder`, který na základě anotací provede validaci modelu a nastaví případně jeho stav na nevalidní. O víc už se starat nemusíme. Kde tento model binder získat a další potřebné informace najdete v článku Brada Wilsona DataAnnotations and ASP.NET MVC.

Protože jsem DataAnnotations použil v jedné aplikaci, tak mám několik zkušeností, o které se můžu podělit. DataAnnotation­sModelBinder má problém s komplexními typy, které mají vlastnosti taktéž komplexních typů. Jedním z možných řešení, je hacknout tento model binder a přidat podmínku na ověření komplexní vlastnosti, nebo používat DTO pouze pro účely formulářů ve view (ano, opět návrat k validaci pouze ve view vrstvě). Anotace podporují vracení chybových hlášek z resource souborů, tak nezapomeňte raději všechny hlášky přepsat, protože ty výchozí nevypadají zrovna nejlépe.

Tak, to bychom měli jeden model binder, který už je hotový. Ale jak si napsat vlastní? To si ukážeme někdy příště na Caste.Componen­ts.Validators­. :)

Vlastní serverové ovládací prvky v ASP.NET MVC

10.32 - 29. března 2009 | Webdesign

Motto: foreach je fajn na prototypování, ale většinou mu za chvíli dojde dech a svádí k zanášení příliš logiky do kódu šablony. A to je špatně.

Už jsem se zmiňoval o tom, že v ASP.NET MVC lze užívat serverové ovládací prvky, takže je využijeme k zapouzdření složitější zobrazovací logiky.

Motivace

Jednou z hlavních nevýhod, kterou jsem viděl na MVC, byl přístup ke skládání šablon. Jednotlivé kusy šablony jsou rozházeny v MasterPage, ViewPage (aspx) a ViewUserControlech (ascx). V Atlasu jsme razili teorii, že šablona by měla být pokud možno co nejcelistvější, aby se nemuselo nikde nic hledat a kodér rychle udělal potřebné změny. Proto existovala sada serverových ovládacích prvků, které byly plně šablonovatelné.

Když naběhlo CHEllou, nechápali jsme, jak to může někdo používat. Každičká část šablony byla (v té době, jak je to dnes – nevím) byla rozeseta v hierarchii složek (podle dědičnosti). A to je v podstatě hlavní rozdíl mezi ASP.NET a architekturou MVC. Tedy pokud jste omezeni view enginem, který používáte.

Již nějaký pátek pracuji na web 2.0 aplikaci, která je postavená mj. na ASP.NET MVC. Měl jsem tedy možnost vyzkoušet hodně možných postupů: od logiky v šabloně, přes HTML helpery, RenderPartial a RenderAction až po vlastní serverové ovládací prvky. A ty nakonec vítězí na plné čáře! Pojďme se podívat, jak si napsat elegantní serverové ovládací prvky pro ASP.NET MVC.

Evoluce logiky v šabloně

Začínal jsem s nadšením s jednoduchou logikou v kódu, tak jak to vidíte v ukázkách či na prezentacích o ASP.NET MVC. Složitější věci jsem se snažil přesunout do HtmlHelperu pomocí vlastních extenzí. Když jsem pak narazil na helpery, které zanáší do view „lambda hell“, trochu mě zamrazilo. Vyberu jen dva příklady: Philův Code based Repeater for ASP.NET MVC a Jardův Simple MVC controls. Nebojte, podobných programátorských krás najdete povícero. Bohužel, je to nepoužitelné pro kodéra. Navíc jsem zastáncem myšlenky minima kódu v šabloně.

Další věcí, která mě tak trochu děsí, je jakým způsobem se předvádí generování HTML formuláře.

<% using(Html.BeginForm("Send", "Comments")) { %>
  <!-- prvky formuláře -->
<% } %>

Je to krásná ukázka užití vzoru IDisposable, ale do šablony nepatří. Helpery uživát jen jako dobré koření – po špetkách.

<form action='<%= Url.Action("Send", "Comments")) %>' method="post">
  <!-- prvky formuláře -->
</form>

Myslím, že takovýto zápis je mnohem srozumitelnější a přitom dělá to samé. Jako bonus můžete ve vašem HTML editoru využívat scope collapsing. Vhodnější by ještě bylo použít helper Url.RouteUrl, který hledá routy podle klíče a tudíž je o dost výkonnější (pokud máte definováno více rout).

Opusťme teď formulář a pokročme k vypisování dat.

Prvním způsobem, jak vypisovat data, je foreach. Je silně typový, což považuju za obrovskou výhodu, a nepřináší overhead v podobě instanciování tříd a parsování šablon serverových ovládacích prvků.

<ul>
<% foreach (var user in Model.Users) { %>
<li><%= user.Name %></li>
<% } %>
</ul>

Tím však jeho možnosti končí. Pokud potřebujeme např. odlišit každou druhou položku, nebo vypsat něco jiného, pokud nejsou žádná data, musíme kód znepřehledňovat, nebo zvolit jiné řešení. Philův repeater už jsem zmiňoval. Další možností je využít asp:Repeater nebo mvc:Repeater. Ani jeden mi nevyhovuje. První se musí nějak nalít daty a pak s nimi svázat (nutnost codebehind nebo script runat=server), druhý zase pracuje s ViewData slovníkem a evalováním. Takže nezbývá než si napsat vlastní.

Silně typový repeater v ASP.NET MVC

Základem je jednoduchá myšlenka. Použít MvcControl z futures a view model opatřit kontrakty.

public interface IHaveUsers {
  IEnumerable<User> Users { get; }
}

public class UsersListViewData : IHaveUsers {
  public IEnumerable<User> Users { get; set; }
  // další vlastnosti view modelu
}

Zavedli jsme si view model třídu, která se nejspíš bude posílat na pohled Index řadičem UsersController. Možná. Každopádně jsme si zavedli jednoduchou abstrakci a možnost znovupoužití v podobě rozhranní IHaveUsers. Snad můžeme dál.

using Microsoft.Web.Mvc;
using Rarous.Web.UI;

[ParseChildren(true)]
public partial class UsersRepeater : MvcControl, ILayoutTemplateable {

  [DefaultValue(typeof(ITemplate), "")]
  [PersistenceMode(PersistenceMode.InnerProperty)]
  [TemplateContainer(typeof(IGenericContainer<User>))]
  [TemplateInstance(TemplateInstance.Multiple)]
  public ITemplate ItemTemplate { get; set; }

  [DefaultValue(typeof(ITemplate), "")]
  [PersistenceMode(PersistenceMode.InnerProperty)]
  [TemplateContainer(typeof(IGenericContainer<User>))]
  [TemplateInstance(TemplateInstance.Multiple)]
  public ITemplate AlternatingItemTemplate { get; set; }

  [DefaultValue(typeof(ITemplate), "")]
  [PersistenceMode(PersistenceMode.InnerProperty)]
  [TemplateContainer(typeof(INamingContainer))]
  [TemplateInstance(TemplateInstance.Multiple)]
  public ITemplate SeparatorTemplate { get; set; }

  public string LayoutContainerId { get; set; }

  [DefaultValue(typeof(ITemplate), "")]
  [PersistenceMode(PersistenceMode.InnerProperty)]
  [TemplateContainer(typeof(INamingContainer))]
  [TemplateInstance(TemplateInstance.Single)]
  public ITemplate LayoutTemplate { get; set; }
}

Podědili jsme si MvcControl, který mimo jiné zpřístupňuje ViewData, a implementovali nějaké šablony.

public partial class UsersRepeater {
  private class UsersViewDataFetcher {
    public IEnumerable<User> GetUsers(object model) {
      var result = model as IHaveUsers;
      if (result != null) {
        return result.Users;
      }
      return null;
    }
  }
}

Jednoduchý helper pro získávání dat z modelu. Zkouší využít kontraktu IHaveUsers, který jsme si zavedli výše, k získání dat z modelu. Zde je místo pro budoucí rozšíření o další možné zdroje. Pokud nic nenajdeme, vrátíme null.

public partial class UsersRepeater {
  protected override void OnPreRender(EventArgs e) {
    base.OnPreRender(e);

    var fetcher = new UsersViewDataFetcher(ViewData.Model);
    var users = fetcher.GetUsers();
    if (users == null) {
      return; // nebo verenderovat NoDataTemplete
    }

    Controls.Clear();

    Control layoutContainer = TemplatingHelper.CreateLayoutContainer(this, this) ?? this;

    var iterator = new ItemsIterator<User>(users);
    foreach (var user in iterator.Iterate()) {
      if (iterator.IsFirst == false) {
        TemplatingHelper.Instantiate(new EmptyContainer(), SeparatorTemplate, layoutContainer);
      }
      ITemplate template = iterator.IsAlternate ? AlternatingItemTemplate ?? ItemTemplate : ItemTemplate;
      TemplatingHelper.Instantiate(new GenericContainer<User>(user), template, layoutContainer);
    }

    layoutContainer.DataBind();
  }
}

Nakonec přepíšeme metodu OnPreRender, ve které získaná data proměníme pomocí šablon na výstupní kód. Používám zde spoustu věcí, které jsem použil již dříve. Jedinou novinkou je třída ItemsIterator, která zaobaluje logiku, pro zjišťování, zda jde o první prvek, alternativní prvek a počítá aktuální index prvku.

public class ItemsIterator<T> {

  private readonly IEnumerable<T> _items;

  public ItemsIterator(IEnumerable<T> items)
    : this(items, 0) {
  }

  public ItemsIterator(IEnumerable<T> items, int firstIndex) {
    _items = items;
    IsFirst = true;
    IsAlternate = false;
    CurrentIndex = firstIndex;
  }

  public bool IsFirst { get; private set; }
  public bool IsAlternate { get; private set; }
  public int CurrentIndex { get; private set; }

  public IEnumerable<T> Iterate() {
    foreach (var item in _items) {
      yield return item;
      IsFirst = false;
      IsAlternate = !IsAlternate;
      CurrentIndex++;
    }
  }
}

Jednoduchá věcička, která je určená k eliminaci otrocky opakovaného kódu.

Užití takového repeateru je pak jednoduché:

<rarous:UsersRepeater runat="server" LayoutContainerId="UsersPlaceHolder">
  <LayoutTemplate>
    <ul>
      <asp:PlaceHolder ID="UsersPlaceHolder" runat="server"/>
    </ul>
  </LayoutTemplate>
  <ItemTemplate>
    <li><%# Container.DataItem.Name %></li>
  </ItemTmplate>
</rarous:UsersRepeater>

Závěr

Snažil jsem se sdělit svůj názor, že v šablonách by mělo být jen tolik programového kódu, kolik je nezbytně nutné. Zároveň se držet zásady jediné zodpovědnosti tříd a znuvupoužitelnosti s využitím generického pomocníka pro iteraci a kontraktů ve view modelu. Zároveň maximálně využít kód, který jsem už psal v předchozích spotech. Je možné, že jsem nepoužil dostatečně kvalitní názvy tříd nebo metod, připomínky klidně piště i k nim.

Zdroje o ASP.NET MVC pro začátečníky

18.51 - 11. března 2009 | Webdesign

Možná hledáte nějaký materiál, který vám pomůže začít s ASP.NET MVC. Pojďme se podívat, jaké máte možnosti.

Knihy

Asi nejčastějšími studijními materiály jsou knihy. Protože je ASP.NET MVC ještě hodně mladý framework, který ještě nebyl oficiálně vydán (produkční verze), žádná kniha ještě nevyšla. Ale minimálně na dvou se usilovně již několik měsíců pracuje. Ještě lepší zprávou je, že jejich rozpracované verze jsou už nyní k dispozici!

Za první knihou ASP.NET MVC Framework Unleashed stojí Senior Program Manager ASP.NET MVC a autor jedné z nejprodávanějších knih o ASP.NET  – Stephen Walther. Jednotlivé kapitoly publikuje na svém blogu. Ty budou po jejím dokončení z blogu staženy. Publikovány byly zatím tyto kapitoly:

  1. Chapter 1 – An Introduction to ASP.NET MVC
  2. Chapter 2 – Building a Simple ASP.NET MVC Application
  3. Chapter 3 – Understanding Controllers
  4. Chapter 4 – Understanding Views
  5. Chapter 5 – Understanding Models
  6. Chapter 6 – Understanding HTML Helpers
  7. Chapter 9 – Understanding Routing

Další knihou je Professional ASP.NET MVC 1.0 z rukou pánů Scott Guthrie, Phil Haack, Scott Hanselman a Rob Conery. Pokud nevíte, kdo jsou tito pánové zač, tak byste to měli rychle dohnat. ScottGu je autorem ASP.NET a dnes je vicepresidentem vývojářské divize Microsoftu. Phil je Senior Program Manager ASP.NET MVC, Routingu a integrace dynamických jazyků do ASP.NET. ScottHa byl první, kdo loni na MIXu ASP.NET MVC presentoval a Rob Conery je autorem ukázkové aplikace – a především poučných screencastů z její tvorby – StoreFront, která je postavená na MVC frameworku. Z knihy byla zatím uvolněna první kapitola, která je průvodcem krok za krokem jak začít stavět aplikaci s ASP.NET MVC.

Ukázkové aplikace

Pokud se vám dobře učí čtením zdrojových kódů, není nic lepšího, než si zdrojové kódy ASP.NET MVC stáhnout na CodePlexu. Z nich můžete pochytit ledasco, ale jak s tím dělat aplikace už moc ne. Proto tu jsou ukázkové aplikace. Tou první a asi nejrozsáhlejší je MVC Storefront. Jenže její zdrojáky byly chvíli na CodePlexu, pak je Rob smazal a slibuje, že brzy budou nové – aktuální. Další ukázková aplikace vznikla pro potřeby druhé jmenované knihy a jmenuje se Nerddinner.

Webcasty

Když ještě nejsou k MVC Storefront zdrojáky, tak vězte, že k němu je asi pětadvacet screencastů! Rob zde provádí vývojem e-shopu. Během té doby prozkoumává různá zákoutí webového vývoje, vylepšuje design aplikace, užívá užitečných komponent atd. Rozhodně stojí za zhlédnutí.

Další videa, která vám snad pomohou s MVC frameworkem začít, najdete přímo na stránkách ASP.NET MVC.

Záznamy přednášek

Doporučení hodné jsou záznamy z loňského MIXu a PDC, konkrétně tyto:

  1. Developing ASP.NET Applications Using the Model View Controller Pattern
  2. ASP.NET MVC : A New Framework for Building Web Applications

Na letošním MIX, který bude již příští týden, bude asi 5 přednášek věnovaných ASP.NET MVC a očekává se, že tam bude také uvedena jeho finální verze.

Na loňském WebExpu jsem měli s Borkem přednášku o ASP.NET Futures a o MVC jsem také mluvili. Přednáška se natáčela, a pokud bude záznam zveřejněn, tak tam možná najdete pár zajímavých informací. :)

Blogy

Zatím největším zdrojem a zároveň nejroztříštěnějším jsou blogspoty. Nejvíc informací najdete na blozích již výše zmiňovaných pánů. Do jejich společnosti se rád vloudím i se svojí troškou do mlýna, ale troufnu si tvrdit, že v některých už jde o trošičku pokročilejší témata a pro jejich pochopení byste už něco měli mít načteno a nazkoušeno. :)

No koukám, že se mi tady z toho stává pěkná linkfarma, tak to raději ukončím a doufám, že se máte od čeho odrazit…

Serverové ovládací prvky v ASP.NET MVC

12.32 - 21. února 2009 | Webdesign

Docela často se vyskytující fámou je, že v ASP.NET MVC nelze používat serverové ovládací prvky (Server Controls). Pravda to je jen z části, nelze používat jen ty prvky, které pro svoji funkčnost vyžadují ViewState. Pojďme se podívat na ty, které fungují i v ASP.NET MVC a můžou nám poskytnout nějakou zajímavou funkcionalitu a zpřehlednit tak naše View.

MasterPage a Placeholdery

Snad první komponenty, které použijete, jsou asp:ContentPlaceHolder a asp:Content. Narazíte na ně při definování globální šablony (Master Page) a jednotlivých pohledů (View). Většinou je použijete automaticky a ani si neuvědomíte, že jde o serverové komponenty. A to je dobře. Rozhodně je lepší používat master page než server side include nebo do všech stránek vkládat view user controly se sdílenými částmi šablony.

LoginView a spol.

Další rozhodně doporučitelnou komponentou je asp:LoginView. Ta slouší k rozdílnému zobrazování obsahu pro nepřihlášené/přih­lášené uživatele a také pro jednotlivé uživatelské role. Předpokladem je, že využíváte ASP.NET Membership nebo nějakou vlastní implementaci, která nastavuje do kontextu správné IPrincipal. Pak můžete v kódu použít třeba toto:

<asp:LoginView ID="LoginView1" runat="server">
  <AnonymousTemplate>
    Nepřihlášený | <%= Html.ActionLink("Přihlásit se", "Login", "Account") %>
  </AnonymousTemplate>
  <LoggedInTemplate>
    <asp:LoginName runat="server" />| <%= Html.ActionLink("Odhlásit se", "Logout", "Account") %>
  </LoggedInTemplate>
</asp:LoginView>

Tím docílíte zobrazení odkazu pro přihlášení nepřihlášenému uživateli a odkazu pro odhlášení uživateli přihlášenému. Jak vidíte, je možné používat i asp:LoginName pro výpis jména aktuálně přihlášeného uživatele.

Localize pro lokalizaci

Pokud děláte na webové aplikaci, která má být lokalizovaná do více jazyků, jistě se vám bude hodit asp:Localize, které slouží k snadnému definování míst, která mají být nahrazována texty z resource souboru. Nebudu teď řešit jak rozhodnout jaký jazyk zvolit, můžete ho mít natvrdo nastavený ve web.config souboru pro jednotlivé deployované domény, nebo získat nějaký parametr z routy, nebo nechat zjišťovat jazyk podle uživatelova nastavení prohlížeče. Pokud budete mít zájem o problematiku lokalizace, můžu jí věnovat některý další spot. :)

<asp:Localize runat="server" meta:resourcekey="SomeResource1" />

Lokalizaci pak provedeme v patřičném resx souboru následovně:

<data name="SomeResource1.Text" xml:space="preserve">
  <value>Some text</value>
</data>

SiteMapPath pro drobečkovou navigaci

Další serverovou komponentou, kterou můžete ve svých šablonách pohledů využít je asp:SiteMapPath, které slouží ke generování drobečkové navigace podle struktury definované v souboru Web.sitemap.

<asp:SiteMapPath runat="server">
  <PathSeparatorTemplate> &gt; </PathSeparatorTemplate>
</asp:SiteMapPath>

Xml pro transformaci

Jedním z posledních ze standardně dodávaných serverových komponent, které ještě v MVC fungují je asp:Xml, které slouží ke vkládání XML dat a jejich transformaci pomocí XSLT.

<asp:Xml runat="server" DocumentSource="/content/contact.xml" TransformSource="/content/contact.xslt"/>

ViewType pro silný typ modelu v pohledu

RC verze MVC frameworku přinesla vlastní serverovou komponentu, která určuje silný typ pro vlastnost Model vašeho pohledu. Je to alternativa k dědění z generické ViewPage.

<mvc:ViewType runat="server" TypeName="ViewPage&lt;MyProject.Model.Customer&gt;" />

A dál?

Dál můžete používat komponenty třetích stran, nebo vlastní, které nevyžadují ViewState. Pokud máte jako předka vašich stránek System.Web.Mvc.ViewPage nemáte v šablonách dostupný data binging. I na to je třeba dávat pozor. Do budoucna by mělo dojít k tomu, že helpery, které jsou součástí MVC frameworku, se dočkají i zapouzdření do komponent. Ale k tomu dojde až po vydání verze 1.0. Již dnes však můžete najít jejich předběžnou implementaci v tzv. ASP.NET MVC Futures, které si můžete stáhnout na CodePlexu. Najdete mezi nimi tyto komponenty:

  • ActionLink,
  • DropDownList,
  • TextBox,
  • Password,
  • Label,
  • Hidden a
  • Repeater

Jejich předkem je MvcControl, který zpřístupňuje ViewContext a ViewData. Repeater si třeba zajišťuje DataBinding sám, takže není třeba ho explicitně volat ze stránky. Nevýhoudou však je, že je třeba Evalovat data, protože Repeater není silně typový. Mám takový pocit, že můj generický repeater by měl v MVC fungovat bez problémů. Ještě jsem to nezkoušel, ale nevidím důvod, proč by neměl. Asi provedu revizi a připravím vylepšenou variantu pro MVC.

A to je pro dnešek vše.

Užití repository v řadičích

10.18 - 25. ledna 2009 | ASP.NET 2.0

Dost už bylo infrastruktury, pojďme si ji trochu užít! :) Dneska si ukážeme jak používat naší infrastrukturu, která by nám měla spoustu věcí usnadnit. Proto jí vlastně tady tvořím.

Dependency injection a bindování modelu

Vytvoříme si řadič, který bude pracovat řekněme s uživateli – to je taková věc, která je nutná snad v každé aplikaci. Ruku na srdce, aplikace bez uživatelů je tak trochu k ničemu. Tak pojďme na to!

[HandleErrors]
public class UsersController : System.Web.Mvc.Controller {

  private IUsersRepository _usersRepository;

  public UsersController(IUsersRepository usersRepository) {
    _usersRepository = usersRepository;
  }

  [ActionName("Rest")]
  [AcceptVerbs(HttpVerbs.Post)]
  public ActionResult Create(FormCollection forms) {

    var user = new User();
    if (ModelState.IsValid == false) {
      throw new ValidationException();
    }

    UpdateModel(user, forms.ToValueProvider());
    _usersRepository.Save(user);

    return Json(user);
  }
}

Krátké a úderné. Jde o klasický řadič, který má závislost na repository uživatelů. Tuto závislost si nechává nainjektovat přes konstruktor. O její životní cyklus se nestará, to řeší IoC kontejner. Pak tu máme akci, která zpracovává POST dotazy řekněme na URL /Users, to teď není podstatné. Tato akce by měla vytvořit nového uživatele z dat, která přišla v POSTu (FormCollection).

Pokud nejsou data v pořádku, tak vyhodíme validační výjimku. O to, jak se tato data mapují na model uživatele, se opět řadič nestará, to je věc model binderu. Pokračujeme dále k uložení uživatele, někam. Kam a jakým způsobem je řadiči opět šumák, to je starost repository. Nakonec pošleme zpátky hotového uživatele jako JSON objekt.

A to je pro dnešek vše… :)