Rails' respond_to in ASP.NET MVC
This post was originally published on Coding Glamour.
In Ruby on Rails is het mogelijk om met respond_to een actie beschikbaar te maken in andere formaten
dan HTML met één regel. Pretty neat, omdat je je code zo zonder
moeite via verschillende interfaces kan gebruiken.
respond_to do |format|
format.html
format.xml { render :xml => @huis }
format.json { render :json => @huis }
end
In ASP.NET MVC 2 is er niet standaard zo'n oplossing, maar doordat
MVC zo pluggable is is deze wel eenvoudig toe te voegen.
Models en Views
Hierbij introduceer ik jullie tot de nieuwe site 'fudna'. Ze
tonen huizen en hebben hiervoor de volgende MVC structuur.
De actie 'Index' op de 'HuisController' ziet er zo
uit:
public ActionResult Index(int id)
{
var model = FudnaDao.GetHuis(id);
return View(model);
}
Wat resulteert in de weergave van dit vernieuwende concept:
En nu andere formaten
Allereerst breiden we de standaard routing uit in Global.asax. Naast de
'standaard' regel voegen we een rule toe die extensies accepteert.
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
// dit is nieuw
routes.MapRoute(
"DefaultWithExtension",
"{controller}/{action}/{id}.{format}",
new { controller = "Home", action = "Index" } // Parameter defaults
);
// dit niet meer
routes.MapRoute(
"Default",
"{controller}/{action}/{id}",
new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
);
}
Om aan te geven welke formaten we ondersteunen maken we gebruik van
ActionFilterAttributes. Mooi hieraan is dat we het resultaat van elke
actie kunnen overschrijven in deze filters. Het RespondTo filter zou er
ongeveer zo uit zien:
public class RespondTo : ActionFilterAttribute
{
private Format[] _formats;
public RespondTo(params Format[] formats)
{
_formats = formats;
}
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
// zoek het model op
var model = ((ViewResult) filterContext.Result).ViewData.Model;
// hebben we een format parameter?
object format;
filterContext.RouteData.Values.TryGetValue("format", out format);
// zoja, dan kijk naar de waarde
switch(format as string)
{
// als json niet ondersteunt; dan return
case "json":
if (!_formats.Any(f => f == Format.Json)) return;
// transformeer het model naar Json
// en overschrijf het oude Result
filterContext.Result = new JsonResult { Data = model, JsonRequestBehavior = JsonRequestBehavior.AllowGet };
break;
case "xml":
if (!_formats.Any(f => f == Format.Xml)) return;
// same same voor XML. Maar dat is lastiger in een one-liner
using(MemoryStream ms = new MemoryStream()) // simpele serialization
using(TextWriter tw = new StreamWriter(ms))
{
new XmlSerializer(model.GetType()).Serialize(tw, model);
ms.Seek(0, SeekOrigin.Begin);
// hier zetten we de nieuwe content naar de XML
filterContext.Result = new ContentResult { ContentType = "text/xml", Content = new StreamReader(ms).ReadToEnd() };
}
break;
}
}
}
public enum Format
{
Xml,
Json
}
Actie aanpassen
Aan de bestaande actie hoeven we nu niets meer aan te passen. We zetten
er enkel een nieuw attribuut op:
[RespondTo(Format.Xml, Format.Json)]
public ActionResult Index(int id)
{
var model = FudnaDao.GetHuis(id);
return View(model);
}
Resultaat
En dat was het al. Wanneer we nu '.xml' of '.json'
toevoegen aan de URL krijgen we de data in dat formaat binnen!
There are 7 comments on this article, read them on Coding Glamour.