代码改变世界

Format Filter for ASP.NET MVC — getting a little more RESTful.

2013-05-10 14:07  yezhi  阅读(246)  评论(0)    收藏  举报

I developing http://nsfw4.me, I wanted a closer-to-automated way to add extension based format filtering to the different controller methods. For integration’s sake, I wanted the clients which may adopt using nsfw4.me to have an easy way to interact and understand what the outcomes of those interactions were.  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 to format 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 they want .xml, then I can give them a serialized version of that data to suit their needs.  If someone visits with the default .html (or nothing at all) we can give standard html rendering.

Enter the FormatFilterAttribute class.  This class hangs on a controller method and works its magic in the OnActionExecutedmethod.  It is in this method that we make the determination on how to handle the request.

Note: This filter requires James Newton-King’s JSON 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 after the user code has run.  At this time we determine whether there was request for something other than a standard render and act accordingly.  If a ViewDataKey has been set for the attribute, we look for this object in the ViewDatacollection, if no key is specified, we try getting the data from the Model property of the current ViewData.  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, good or bad in xml format.  The same also applies for .json requests.