Download Source Code
Introduction
In this article we shall see how to build or implement custom ActionResults. One has to go with custom ActionResults if we do not find an existing ActionResult. Take an example of a Controller ActionResult returning a xml data , Rss feeds, Excel data. Here comes the necessity to build one. Depending upon the requirement one has to go with custom implementation. Here we will see with an example how to return a Xml , Rss feeds from controller actions.
Background
Basic understanding and experience in developing a web based application with ASP.NET MVC.
Here's a beginner level article to understand ActionResult - 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 Xml response. One has to extend ActionResult class and override the ExecuteResult. You can see in the ExecuteResult method , we have written code to create a new Xml document by parsing the content , set the encoding and get bytes to return.
Also notice the default encoding is "utf-8" , one can also set the encoding so has 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);
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 demostrate the xml response, The GetXmlData() method just reads the xml file Book.xml then loads the result as XML Document and returns the response as XmlActionResult.
Custom ActionResult to return RSS Feeds
Below is the implementation of a custom feeds ActionResult. The Syndication feeds can be of two types , Atom or Rss. Depending upon on your needs one can return either of the feed 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 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 in keeping in mind of Single Responsiblilty 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 = 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