Click here to Skip to main content
Click here to Skip to main content

Custom ASP.NET MVC ActionResults

By , 23 Sep 2013
Rate this:
Please Sign up or sign in to vote.

Introduction

In this article we shall see how to build or implement custom ActionResults. We have to go with custom ActionResults if we do not find an existing ActionResult. Take an example of a Controller ActionResult returning XML data, RSS feeds, and Excel data. Here comes the necessity to build one. Depending upon the requirement, we have to go with a custom implementation. Here we will see with an example how to return XML, RSS feeds from controller actions. 

Background  

A basic understanding and experience in developing a web based application with ASP.NET MVC.

Here's a beginner level article to understand ActionResults - http://www.codeproject.com/Articles/195434/Actions-in-ASP-NET-MVC-3.

Using the Code 

Custom ActionResult to Return XML Data

The below custom ActionResult (XmlActionResult) is used to return an XML response. We have to extend the ActionResult class and override ExecuteResult. You can see in the ExecuteResult method, we have written code to create a new XML document by parsing the content, setting the encoding, and getting bytes to return.

Also notice the default encoding is "UTF-8". We can also set the encoding so have to return the proper response.

public enum EncodingType
{
    UTF8,
    UTF16,
    UTF32
}

public class XmlActionResult : ActionResult
{
    public XmlActionResult(string xml, string fileName, 
        EncodingType encoding = EncodingType.UTF8 , 
        LoadOptions loadOptions = System.Xml.Linq.LoadOptions.None)
    {
        XmlContent = xml;
        FileName = fileName;
        Encoding = encoding;
        LoadOptions = loadOptions;
    }

    public string FileName
    {
        get;
        set;
    }

    public string XmlContent
    {
        get;
        set;
    }

    public EncodingType Encoding
    {
        get;
        set;
    }

    public LoadOptions LoadOptions
    {
        get;
        set;
    }

    public XmlDocument ToXmlDocument(XDocument xdoc)
    {
        var xmldoc = new XmlDocument();
        xmldoc.Load(xdoc.CreateReader());
        return xmldoc;
    }

    public override void ExecuteResult(ControllerContext context)
    {
        XDocument doc = XDocument.Parse(XmlContent, this.LoadOptions);
        context.HttpContext.Response.ContentType = "text/xml"; 
        context.HttpContext.Response.AddHeader("content-disposition", 
          string.Format("attachment; filename={0}", FileName));
           
        XmlDocument xmldoc = ToXmlDocument(doc);
        // Create an XML declaration. 
        XmlDeclaration xmldecl;
        xmldecl = xmldoc.CreateXmlDeclaration("1.0", null, null);
      
        switch (Encoding)
        {
            case EncodingType.UTF8:
                doc.Declaration = new XDeclaration("1.0", "utf-8", null);
                context.HttpContext.Response.Charset = "utf-8";                  
                xmldecl.Encoding = "UTF-8";
                XmlElement root = xmldoc.DocumentElement;
                xmldoc.InsertBefore(xmldecl, root); 
                context.HttpContext.Response.BinaryWrite(
                  System.Text.UTF8Encoding.Default.GetBytes(xmldoc.OuterXml));           
                break;
            case EncodingType.UTF16:
                doc.Declaration = new XDeclaration("1.0", "utf-16", null);
                context.HttpContext.Response.Charset = "utf-16";
                xmldecl.Encoding = "UTF-16";
                root = xmldoc.DocumentElement;
                xmldoc.InsertBefore(xmldecl, root); 
                context.HttpContext.Response.BinaryWrite(
                  System.Text.UnicodeEncoding.Default.GetBytes(xmldoc.OuterXml));
                break;
            case EncodingType.UTF32:
                doc.Declaration = new XDeclaration("1.0", "utf-32", null);
                context.HttpContext.Response.Charset = "utf-32";
                xmldecl.Encoding = "UTF-32";
                root = xmldoc.DocumentElement;
                xmldoc.InsertBefore(xmldecl, root);
                context.HttpContext.Response.BinaryWrite(
                  System.Text.UTF32Encoding.Default.GetBytes(xmldoc.OuterXml));
                break;
        }          
       
        context.HttpContext.Response.End();
    }
}

Below is the Controller ActionResult implementation:

[HttpGet]
public XmlActionResult GetXmlData()
{
    System.Xml.XmlTextReader reader = 
      new System.Xml.XmlTextReader(Server.MapPath("~/Book.xml"));
    var xml = XElement.Load(reader);
    return new XmlActionResult(xml.ToString(), "book.xml");
}

This is just to demonstrate the XML response. The GetXmlData() method just reads the XML file Book.xml then loads the result as an XML document and returns the response as XmlActionResult.

Custom ActionResult to Return RSS Feeds

Below is the implementation of a custom feed ActionResult. The syndication feeds can be of two types: ATOM or RSS. Depending upon your needs we can return either of the feeds as a response. You can also see below, we are using the formatters to format the data and present it to the client. If you see out the output of the FeedActionResult, it's also an XML response. You may ask a question, why we have not used the XmlActionResult. However it's always good to separate these implementations and this class is designed keeping in mind the Single Responsibility Principle. A class or a module should have one, and only one, reason to change.

You can notice the content type, it is "application/rss+xml".

public enum FeedType
{
    Atom,
    Rss
}

public class FeedActionResult : ActionResult
{
    private SyndicationFeedFormatter formatter;

    public FeedActionResult(SyndicationFeed feed, FeedType feedType)
    {
        switch (feedType)
        {
            case FeedType.Atom:
                formatter = new Atom10FeedFormatter(feed);
                break;
            case FeedType.Rss:
                formatter = new Rss20FeedFormatter(feed);
                break;
        }
    }

    public SyndicationFeed Feed { get; set; }

    public override void ExecuteResult(ControllerContext context)
    {
        context.HttpContext.Response.ContentType = "application/rss+xml";
        using (var writer = XmlWriter.Create(context.HttpContext.Response.Output))
        {
            formatter.WriteTo(writer);
        }
    }
}

Below is the Controller ActionResult implementation which returns the RSS feed.

[HttpGet]
public ActionResult GetRssFeeds()
{
     XmlReader reader = XmlReader.Create(
       "http://www.codeproject.com/webservices/articlerss.aspx",
     new XmlReaderSettings()
     {
         //MaxCharactersInDocument can be used to control the maximum amount of data 
         //read from the reader and helps prevent OutOfMemoryException
         MaxCharactersInDocument = 1024 * 64
     });

     SyndicationFeed feed = SyndicationFeed.Load(reader);         
     var rssFeed = new FeedActionResult(feed, FeedType.Rss);
     return rssFeed;
}

In order to demonstrate the usage of RssActionResult, we have created a Controller Action method: GetRssFeeds(). It reads the RSS feeds published by CodeProject, loads the same as SyndicationFeed, and returns the feeds as a response.

Points of Interest

It is fun in coding the custom ActionResults. Lot of things I learnt while building these custom ActionResults and I hope it should help others too.

Here is an interesting link if you are looking to return a excel data as ActionResult - http://www.dotnetcurry.com/ShowArticle.aspx?ID=484  

History 

  • Version 1.0  - 1/24/2013 - First release with source code. Implemented custom ActionResults.
  • Version 1.1 - 1/25/2013  - Updated XmlActionResult to support for multiple encoding types.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Ranjan.D
Web Developer
United States United States
Profile
 
Around 9 years of professional software development experience in analysis, design, development, testing and implementation of enterprise web applications for healthcare domain with good exposure to object-oriented design, software architectures, design patterns, test-driven development and agile practices.
 
In Brief
 
Analyse and create High Level , Detailed Design documents.
Use UML Modelling and create Use Cases , Class Diagram , Component Model , Deployment Diagram, Sequence Diagram in HLD.
 
Area of Working : Dedicated to Microsoft .NET Technologies
Experience with : C# , J2EE , J2ME, Windows Phone 8, Windows Store App
Proficient in: C# , XML , XHTML, XML, HTML5, Javascript, Jquery, CSS, SQL, LINQ, EF
 
Software Development
 
Database: Microsoft SQL Server, FoxPro
Development Frameworks: Microsoft .NET 1.1, 2.0, 3.5, 4.5
UI: Windows Forms, Windows Presentation Foundation, ASP.NET Web Forms and ASP.NET MVC3, MVC4, WebAPI
Coding: WinForm , Web Development, Windows Phone, WinRT Programming
 
Healthcare Domain Experience
 
CCD, CCR, QRDA, HIE, HL7 V3, Healthcare Interoperability
 
Others:
 
TTD, BDD
 
Education
 
B.E (Computer Science)
 
CodeProject Contest So Far:
 
1. Windows Azure Developer Contest - HealthReunion - A Windows Azure based healthcare product , link - http://www.codeproject.com/Articles/582535/HealthReunion-A-Windows-Azure-based-healthcare-pro
 
2. DnB Developer Contest - DNB Business Lookup and Analytics , link - http://www.codeproject.com/Articles/618344/DNB-Business-Lookup-and-Analytics
 
3. Intel Ultrabook Contest - Journey from development, code signing to publishing my App to Intel AppUp , link - http://www.codeproject.com/Articles/517482/Journey-from-development-code-signing-to-publishin
 
4. Intel App Innovation Contest 2013 - eHealthCare - http://www.codeproject.com/Articles/635815/eHealthCare

Comments and Discussions

 
GeneralMy vote of 4 Pinmembersraoh29-Jan-13 4:17 
GeneralRe: My vote of 4 PinmemberRanjan.D29-Jan-13 4:50 
GeneralMy vote of 5 PinmemberPatrick Harris25-Jan-13 3:45 
GeneralRe: My vote of 5 [modified] PinmemberRanjan.D25-Jan-13 12:57 
Thanks for your vote ! Wish you had a blasting party for your Birthday Smile | :)
 
Thanks,
Ranjan.D


modified 25-Jan-13 19:28pm.

QuestionUTF-8 Encoding PinmemberPhilippe Mori24-Jan-13 12:37 
AnswerRe: UTF-8 Encoding [modified] PinmemberRanjan.D24-Jan-13 12:52 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web02 | 2.8.140421.2 | Last Updated 23 Sep 2013
Article Copyright 2013 by Ranjan.D
Everything else Copyright © CodeProject, 1999-2014
Terms of Use
Layout: fixed | fluid