I developing http://nsfw4.me, I wanted a closer-to-automated way to add extensionbased format filtering to the different controller methods. For integration'ssake, I wanted the clients which may adopt using nsfw4.me tohave an easy way to interact and understand what the outcomes of those interactionswere. In building this site on Microsoft's newly released MVC framework,it was easy to create a RESTful interface, but I didn't see a baked in way toformat output based on requested extension.
The approach I took was create two new route entries in Global.asax.cs:
1:
2: routes.MapRoute("DefaultFormat", "{controller}/{action}.{format}", 3: new { controller = "Home", action = "Index", id = "", format = "html"}); 4:
5: routes.MapRoute("DefaultFormat","{controller}/{action}/{id}.{format}",6: new { controller = "Home", action = "Index",id = "", format = "html"});7:
8:
Now, I would be able to pick up an extension on the requested path and act accordingly. For example, if someone visits a details link for a given object, and theywant .xml, then I can give them a serialized version of that data to suit theirneeds. If someone visits with the default .html (or nothing at all) we cangive a standard html rendering.
Enter the FormatFilterAttribute class. This class hangs on a Controller methodand works its magic in the OnActionExecuted method. It is in this method thatwe make the determination on how to handle the request.
Note: Thisfilter requires James Newton-King'sJSON Library.
1: public override void OnActionExecuted(ActionExecutedContext filterContext)
2: {3: if (filterContext.RouteData.Values.ContainsKey("format"))4: {5: var fmt = GetFormat(filterContext);
6: var obj = GetResultObject(filterContext);
7: switch (fmt)
8: {9: case "xml":
10: if (null != obj)
11: {12: XmlSerializer xs = new XmlSerializer(obj.GetType());
13: using (MemoryStream Ms = new MemoryStream())
14: {15: xs.Serialize(Ms, obj);
16: var ret = System.Text.Encoding.UTF8.GetString(Ms.ToArray());
17: filterContext.Result =new PlainTextActionResult(ret);
18: }
19: }
20: break;
21: case "json":
22: if (null != obj)
23: {24: filterContext.Result =new PlainTextActionResult(
25: JsonConvert.SerializeObject(obj, Formatting.Indented));
26: }
27: break;
28: case "html":
29: //DO NOTHING
30: break;
31: default:
32: throw new ArgumentException("Invalid format supplied");33: break;
34: }
35: }
36: base.OnActionExecuted(filterContext);
37: }
38:
What's happening Here?
As the MVC framework processes the request to this method, we will intercept afterthe user code has run. At this time we determine whether there was requestfor something other than a standard render and act accordingly. If a ViewDataKeyhas been set for the attribute, we look for this object in the ViewData collection,if no key is specified, we try getting the data from the Model property of the currentViewData. This data is then serialized back to the requesting client. Currently,2 formats are supported: xml and json.
Try it here:
HTML: http://nsfw4.me/links/details/yahoo
JSON: http://nsfw4.me/links/details/yahoo.json
XML: http://nsfw4.me/links/details/yahoo.xml
It also works with creating data. If you post the proper values (LinkId,LinkUrl) to http://nsfw4.me/links/create.xml, you will receive the result, goodor bad in xml format. The same also applies for .json requests.