CodePaste Logo
New Snippet New Snippet Recent Snippets Recent Snippets My Snippets My Snippets Web Code Search Snippets Search
Sign inor Register
Language: C#

MVC API Controller that allows for format=xml/json

1012 Views
Copy Code Show/Hide Line Numbers
using System;
using System.Collections.Generic;
using System.Web;
using System.Web.Mvc;
using Westwind.Utilities;
using System.ServiceModel.Syndication;
using CodePasteBusiness;
using System.Text;
using System.Collections;
using System.IO;
using System.Xml;
using Westwind.Web.JsonSerializers;
 
namespace CodePasteMvc.Controllers
{
 
    /// <summary>
    /// API controller that handles results based.
    /// 
    /// Reusable base class.
    /// </summary>    
    public class ApiControllerBase : baseController
    {
        /// <summary>
        /// Used to determine output format - json,xml,rss,atom
        /// </summary>
        protected string Format = string.Empty;
 
 
        protected override void Initialize(System.Web.Routing.RequestContext requestContext)
        {
            // Create a custom Invoker that handles non-actionresult controller methods
            this.ActionInvoker = new ApiActionInvoker();
 
            base.Initialize(requestContext);
 
            // pick up the format
            this.Format = (Request.QueryString["Format"] ?? string.Empty).ToLower();
 
            //if (string.IsNullOrEmpty(this.Format))
            //    this.Format = "xml";
        }
 
        /// <summary>
        /// Checks 
        /// </summary>
        /// <param name="instance"></param>
        /// <returns></returns>
        protected internal virtual ActionResult ApiResult(object instance)
        {
            if (string.IsNullOrEmpty(this.Format))
                return null;
 
            if (this.Format == "json")
                return this.Json(instance);
            else if (this.Format == "xml")
            {
                string xmlResult = string.Empty;
 
                if (!SerializationUtils.SerializeObject(instance, out xmlResult))
                    throw new InvalidOperationException("Unable to serialize instance to Xml");
                return this.Content(xmlResult, "text/xml", Encoding.UTF8);
            }
            else if (this.Format == "rss" || this.Format == "atom")
            {
                return GetFeed(instance);
            }
 
            return null;
        }
 
        /// <summary>
        /// Returns and rss or atom feed 
        /// </summary>
        /// <param name="instance"></param>
        /// <returns></returns>
        protected internal ActionResult GetFeed(object instance)
        {
            string title = "CodePaste.NET";
            string action = this.RouteData.Values["listAction"] as string;
            if (string.IsNullOrEmpty(action))
                action = this.RouteData.Values["Action"] as string ?? string.Empty;
            action = action.ToLower();
 
            if (action == "recent")
                title = "CodePaste.NET Recent Snippets";
            else if (action == "mysnippets")
                title = "CodePaste.NET - My Snippets";
 
            SyndicationFeed feed = new SyndicationFeed(title, "Paste and Link .NET Code", new Uri(Request.Url.AbsoluteUri));
            feed.BaseUri = new Uri("http://codepaste.net/recent");
            feed.LastUpdatedTime = DateTime.Now;
 
            busCodeSnippet snippet = new busCodeSnippet();
 
            List<SyndicationItem> feedItems = new List<SyndicationItem>();
            foreach (CodeSnippetListItem item in (IEnumerable)instance)
            {
                SyndicationItem rssItem = new SyndicationItem()
                {
                    Id = item.Id,
                    Title = SyndicationContent.CreateHtmlContent(item.Title),
                    Content = SyndicationContent.CreateHtmlContent(
                                "\r\n<link href=\"" + WebUtils.ResolveServerUrl("~/css/csharp.css") + "\" rel=\"stylesheet\" type=\"text/css\" />\r\n" +
                                "<pre>" +
                                snippet.GetFormattedCode(item.Code, item.Language, item.ShowLineNumbers) +
                                "</pre>"),
                    PublishDate = item.Entered
                };
                rssItem.Authors.Add(new SyndicationPerson("", item.Author, null));
                rssItem.Links.Add(new SyndicationLink(new Uri(WebUtils.GetFullApplicationPath() + "/" + item.Id),
                                                        "alternate", item.Title, "text/html", 1000));
 
                feedItems.Add(rssItem);
            }
 
            feed.Items = feedItems;
 
            MemoryStream ms = new MemoryStream();
            XmlWriter writer = XmlWriter.Create(ms);
            if (this.Format == "rss")
            {
                Rss20FeedFormatter rssFormatter = new Rss20FeedFormatter(feed);
                rssFormatter.WriteTo(writer);
            }
            else
            {
                Atom10FeedFormatter atomFormatter = new Atom10FeedFormatter(feed);
                atomFormatter.WriteTo(writer);
            }
            writer.Flush();
 
            ms.Position = 0;
 
            return this.Content(Encoding.UTF8.GetString(ms.ToArray()), "application/xml");
        }
 
        /// <summary>
        /// Parses a single object value from the Request input stream into the
        /// specified type
        /// </summary>
        /// <param name="dataType"></param>
        /// <returns></returns>
        protected internal object ParseObjectFromPostData(Type dataType)
        {
            StreamReader sr = new StreamReader(Request.InputStream);
            string data = sr.ReadToEnd();
            sr.Close();
 
            object result = null;
 
            if (Request.ContentType == "text/javascript" || Request.ContentType == "application/json")
            {
                JSONSerializer ser = new JSONSerializer(SupportedJsonParserTypes.WestWindJsonSerializer);
                result = ser.Deserialize(data, dataType);
            }
            else if (Request.ContentType == "text/xml")
            {
                result = SerializationUtils.DeSerializeObject(data, dataType);
            }
            else
                return ExceptionResult("Unsuppported data input format. Please provide input content-type as text/javascript, application/json or text/xml.");
 
            return result;
        }
 
        /// <summary>
        /// Ensure that exceptions are returned in the API format
        /// Excpetion is rendered as a Callback Exception
        /// </summary>
        /// <param name="filterContext"></param>
        protected override void OnException(ExceptionContext filterContext)
        {
            base.OnException(filterContext);
 
            // only handle API results here: Format must be set
            // otherwise fall through and return
            if (string.IsNullOrEmpty(this.Format))
                return;
 
            Response.StatusCode = 500;
 
            Westwind.Web.CallbackException exception = new Westwind.Web.CallbackException();
            exception.message = filterContext.Exception.Message;
 
            if (HttpContext.IsDebuggingEnabled)
                exception.stackTrace = filterContext.Exception.StackTrace;
 
            exception.isCallbackError = true;
 
            filterContext.Result = this.ApiResult(exception);
            filterContext.ExceptionHandled = true;
        }
 
        protected ActionResult ExceptionResult(Exception ex)
        {
            Response.StatusCode = 500;
 
            Westwind.Web.CallbackException exception = new Westwind.Web.CallbackException();
            exception.message = ex.Message;
 
            if (HttpContext.IsDebuggingEnabled)
                exception.stackTrace = ex.StackTrace;
            exception.isCallbackError = true;
 
            // return result based on ?Format= 
            return this.ApiResult(exception);
        }
 
        protected ActionResult ExceptionResult(string message)
        {
            Response.StatusCode = 500;
 
            Westwind.Web.CallbackException exception = new Westwind.Web.CallbackException();
            exception.message = message;
            exception.isCallbackError = true;
 
            // return result based on ?Format=
            return this.ApiResult(exception);
        }
 
    }
 
    /// <summary>
    /// Custom ActionInvoker that allows Controller methods to return plain
    /// value results rather than action results that are formatted using
    /// API formatting rules of an API Controller
    /// </summary>
    public class ApiActionInvoker : ControllerActionInvoker
    {
        /// <summary>
        /// Overrides output creation if the result from a controller
        /// method doesn't return an ActionResult. Instead it turns the
        /// result into the appropriate API response.
        /// </summary>
        /// <param name="controllerContext"></param>
        /// <param name="actionDescriptor"></param>
        /// <param name="actionReturnValue"></param>
        /// <returns></returns>
        protected override ActionResult CreateActionResult(ControllerContext controllerContext, ActionDescriptor actionDescriptor, object actionReturnValue)
        {
 
            // Allow for controllers to return plain values rather than an action result
            // Also handles exceptions from API controller
            if (!(actionReturnValue is ActionResult) && controllerContext.Controller is ApiControllerBase)
            {
                ApiController controller = controllerContext.Controller as ApiController;
 
                // Generate API results in XML/JSON
                return controller.ApiResult(actionReturnValue);
            }
 
            // Process as normal request
            return base.CreateActionResult(controllerContext, actionDescriptor, actionReturnValue);
        }
 
 
    }
}
by Rick Strahl
  August 10, 2009 @ 4:44pm
Tags:

Add a comment


Report Abuse
brought to you by:
West Wind Techologies



If you find this site useful and use it frequently please consider making a donation to support this free service.
Donate