Provedení přesměrování je ve webovém programování zcela běžná věc a pokud chceme programovat podle návrhového vzoru Post/Redirect/Get, pak je to přímo nutnost. Vřele doporučuji tento vzor používat, protože díky němu můžeme zabránit vícenásobnému odeslání formuláře (známé double-posty z fór a komentářů) a jeho použití vede také k lepšímu oddělení koncernů (zájmů, oblastí aplikací sloužící k různému účelu). A v neposlední řadě je jeho implementace triviální 😉 Použití tohoto návrhového vzoru v ASP.NET MVC aplikaci je velmi jednoduché, protože sám o sobě nevyžaduje žádnou speciální podporu. V akční metodě zpracovávající POST data prostě místo obligátního „return View(…)“ zavoláme „return RedirectToAction(…)“ nebo „return RedirectToRoute(…)„.
Při přesměrování chceme ale často do cíle přesměrování předat nějakou informaci, klasicky hlášku „Produkt byl úspěšně aktualizován.“ apod. Jak nejlépe tuto hlášku, resp. jakákoliv data předat do cíle přesměrování? Když se blíže podíváme na metody RedirectToAction a RedirectToRoute třídy Controller, tak zjistíme, že obě vždy vrací instanci třídy RedirectToRouteResult. Nejdůležitější položkou této třídy je pro nás v tuto chvíli property Values, která je typu RouteValueDictionary, což je jen potomek třídy Dictionary<string, object>.
Náš pohled tedy můžeme zjednodušit a říkat si, že metody RedirectToAction a RedirectToRoute vrací kolekci párů klíč – hodnota, které budou použity pro konstrukci odkazu, na který bude provedeno přesměrování. Přesněji řečeno jsou to prakticky jediná vstupní data pro metodu GetVirtualPath třídy Route, která se používá právě k překladu párů klíč – hodnota na URL, což se děje při spuštění výsledku, tedy volání metody ExecuteResult třídy RedirectToRouteResult. Tam dojde opravdu jen k překladu párů klíč – hodnota na URL a provede se Response.Redirect na získanou URL – není za tím vůbec žádná alchymie.
Když budeme používat klasické routování (controller/action/id), tak nám stačí umístit do kolekce hodnotu pro klíč „controller„, „action“ a případně „id„. A to je přesně to, co dělá metoda RedirectToAction! Ta jen nainicializuje kolekci párů klíč – hodnota, přidá do ní hodnoty pro tyto klíče a zavolá metodu RedirectToRoute.
Nyní jsme si řekli, jak probíhá klasické přesměrování v ASP.NET MVC. Teď se podívejme na to, jak předat při přesměrování nějaká data. Inu není to nic složitého a bystřejší se již určitě dovtípili, že kýženého výsledku dosáhneme přidáním vlastních párů klíč – hodnota do kolekce, kterou vrací metody RedirectToAction a RedirectToResult. Přesněji řečeno máme k dispozici přetížené varianty těchto metod, které nám umožní elegatně přidat další páry klíč – hodnota pro routování. Tyto hodnoty jsou většinou v URL obsaženy ve formě query stringu.
Hlášku „Produkt byl úspěšně aktualizován.“ můžeme přidat např. takto:
var values = new System.Web.Routing.RouteValueDictionary();
values.Add("message", "Produkt byl úspěšně aktualizován.");
return RedirectToAction("MyAction", "MyController", values);
Trošku hodně kódu kvůli takové prkotince, nemyslíte? 😉 Proto použijme trik hojně využívaný v celém ASP.NET MVC:
return RedirectToAction("MyAction", "MyController", new { message = "Produkt byl úspěšně aktualizován." });
Zde používám jinou přetíženou variantu metody RedirectToAction, která bere místo třídy RouteValueDictionary instanci typu object. V takovém případě, když object zastupuje dictionary, se projdou všechny properties daného objectu a názvy properties se použijí jako klíče, hodnoty properties jako hodnoty. Za povšimnutí stojí, že je zde velmi výhodné použít anonymní třídy a není tedy třeba deklarovat další typ. Celý tento proces transformace jakékoliv třídy do kolekce párů klíč – hodnota, lze popsat pár řádky kódu:
public void AddValues(object values)
{
if (values != null)
{
foreach (PropertyDescriptor descriptor in TypeDescriptor.GetProperties(values))
{
this.Add(descriptor.Name, descriptor.GetValue(values));
}
}
}
Ale zpět k tématu. Předali jsme tedy routingu nějaké vlastní informace ve formě párů klíč – hodnota a ten je velmi pravděpodobně zakuklil do query stringu. Z toho vyplývá, jak příchozí data načíst v cíli přesměrování – protože defaultní value-provider čte kromě informací z routingu (controller, action, id) a form dat také query string, tak velmi intuitivně. Můžeme obsluhující akční metodě dát parametr „message“ typu string, můžeme si hodnotu přečíst pomocí ValueProvider.GetValue(„message“) nebo na hodnotu „message“ spoléhat při model-bindingu.
Předávání dat přes query string se ale nemusí každému líbit a jistě má i svá negativa (např. člověk si může URL změnit, aby se mu vypisovala jako hláška libovolný string). Pro předávání dat v těchto situacích existuje v ASP.NET MVC připravené řešení – kolekce párů klíč-hodnota TempData dostupná v Controlleru. Data uložená do této kolekce přežijí právě jen po dobu mezi dvěma requesty. A kde jsou tato data uložena? O to se stará rozhraní ITempDataProvider, resp. property TempDataProvider třídy Controller. Pokud nic nezměníme, bude se používat SessionTempDataProvider, který jak název napovídá ukládá data do session (takže logicky je potřeba mít session zapnutou). Součástí MVC Futures je ale také třída CookieTempDataProvider, která se může hodit, pokud např. z nějakého důvodu nechceme nebo nemůžeme session zapínat.
Pokud chceme používat jiný temp-data-provider než ten využívající session, tak nejjednodušším řešením je ve svém Controlleru přerajdovat metodu Initialize a v ní přiřadit do property TempDataProvider instanci nějaké třídy implementující rozhraní ITempDataProvider.
Je tedy jen na Vás, jaký způsob předání dat při přesměrování využijete. Btw. toto je jedna z věcí, které se mi na ASP.NET MVC líbí – nejste téměř ničím omezeni a hodně si toho můžete udělat podle sebe. Jen dodám, ž k dopsání posledního odstavce mě vyprovokovala diskuze pod tímto článkem.