Learn ASP.NET MVC, ASP.NET Core, and Design Patterns through our online training programs. Courses conducted by Bipin Joshi on weekends. Read more details here.

Creating HTTP Handler Factory for serving RSS and ATOM feeds

Introduction

ASP.NET has its own set of file extensions such as .aspx, .ascx and .asmx. What if you want to create resources that have custom extensions? Http handlers allow you do just that. In this article we will create two custom extensions - .rss and .atom - that will be handled by two HTTP handlers. To simplify the configuration we will further create HTTP handler factory that decides which handler to invoke.

What are HTTP handlers?

HTTP handlers are nothing but classes intended for processing a custom file extension. They are similar to ISAPI extentions. Internally they implement IHttpHandler interface. HTTP handlers are useful when the resource you are creating is highly customized and web forms can not present a neat solution. For example, let's say you want to expose RSS and ATOM feeds of your web log to the external world. Sure you can do that using normal web forms. However, since you are not rendering any user interface as such why incur overhead of page level events? Your task can be best accomplished by a custom handler that will emit RSS or ATOM feeds directly onto the raw response stream. This way you can avoid the overhead mentioned above as well as you will be able to design a more readable and meaningful extension for your resources (say .rss for RSS feeds and .atom for ATOM feeds).

What is HTTP Handler Factory?

HTTP handler factory is nothing but a class that implements IHttpHandlerFactory interface. Handler factories come handy when there are many HTTP handlers and you do not know the exact HTTP handler to use untill runtime. Handler factories also reduce your configuration management (in IIS as well as Web.config) because all you need to configure is the factory assembly and not the individual handlers.

The IHttpHandler and IHttpHandlerFactory interfaces

The IHttpHandler interface is implemented by all the handlers. The interface consists of one property called IsReusable. The IsReusable property gets a value indicating whether another request can use the IHttpHandler instance. The method ProcessRequest() allows you to process the current request. This is the core place where all your code goes. This method receives a parameter of type HttpContext using which you can access the intrinsic objects such as Request and Response.

The IHttpHandlerFactory interface consists of two methods - GetHandler and ReleaseHandler. The GetHandler() method instantiates the required HTTP handler based on some condition and returns it back to ASP.NET. The ReleaseHandler() method allows the factory to reuse an existing handler.

Creating a sample database

Begin by creating new IIS based web site called HandlerFactoryWeb. Add a new SQL Server database to it called Database.mdf. Create a table called Articles with the following schema:

Creating RSS feed generator

Add a new project of type class library called FeedHandlerLib. Add a class called RSSFeedGenerator. Add the following code to the class.

using System;
using System.Collections.Generic;
using System.Text;
using System.Data;
using System.Data.SqlClient;
using System.Configuration;
using System.Xml;
using System.IO;

namespace FeedHandlerLib
{
public class RSSFeedGenerator
{
public static string GetRSS()
{
MemoryStream ms = new MemoryStream();
XmlTextWriter writer = new XmlTextWriter(ms, null);
SqlConnection cnn = new SqlConnection
(ConfigurationManager.ConnectionStrings["connstr"].
ConnectionString);
SqlCommand cmd = new SqlCommand("select * from 
articles order by pubdate desc", cnn);
cnn.Open();
SqlDataReader reader = cmd.ExecuteReader();
writer.WriteStartElement("rss");
writer.WriteAttributeString("version", "2.0");
writer.WriteStartElement("channel");
writer.WriteElementString("title", "MyWebSite.com");
writer.WriteElementString("link", "http://mywebsite.com");
writer.WriteElementString("description", "Cool articles...");
writer.WriteElementString("copyright", "Copyright (C) 2006");
writer.WriteElementString("generator", 
"DotNetBips.com RSS Generator");
while (reader.Read())
{
writer.WriteStartElement("item");
writer.WriteElementString("title",
 reader.GetString(reader.GetOrdinal("title")));
writer.WriteElementString("link",
 reader.GetString(reader.GetOrdinal("url")));
writer.WriteElementString("description",
 reader.GetString(reader.GetOrdinal("description")));
writer.WriteElementString("pubDate",
 reader.GetDateTime(reader.GetOrdinal("pubdate")).
ToString(@"ddd, dd MMM yyyy 12:00:00 tt G\MT"));
writer.WriteEndElement();
}
writer.WriteEndElement();
writer.WriteEndElement();
reader.Close();
cnn.Close();
writer.BaseStream.Flush();
writer.Flush();
ms.Flush();
byte[] data = new byte[ms.Length];
ms.Seek(0, SeekOrigin.Begin);
ms.Read(data, 0, data.Length);
ms.Close();
return ASCIIEncoding.ASCII.GetString(data);
}
}
}

We will not discuss the RSS format in details here. You can read Creating RSS feeds for your web site and Consuming RSS feeds on your web site for more details on RSS file format.

The RSSFeedGenerator class has a static method called GetRSS(). The GetRSS() method  selects all the articles from the database and generates an RSS feed out of it. It first serializes all the data in a MemoryStream instance with the help of XmlTextWriter class. Then this data is converted into a string and returned back to the caller.

The following figure shows a sample of the RSS feed generated by this class.

Creating RSS Handler

Now that we have RSS generator class ready, let's create the RSS handler that will handle .rss extension. Add a new class called RSSHandler and write the following code to it:

using System;
using System.Collections.Generic;
using System.Text;
using System.Web;
using System.Data;
using System.Data.SqlClient;
using System.Configuration;
using System.Xml;
using System.IO;

namespace FeedHandlerLib
{
public class RSSHandler:IHttpHandler
{
public bool IsReusable
{
get 
{
return false;
}
}

public void ProcessRequest(HttpContext context)
{
context.Response.ContentType = "text/xml";
string str = RSSFeedGenerator.GetRSS();
context.Response.Write(str); 
}

}
}

The class implements IHttpHandler interface. The IsReusable returns false indicating that we don't want to reuse the instance of the handler. The ProcessRequest sets the ContentType of the response stream to text/xml. It then calls GetRSS() method of RSSFeedGenerator class. The returned string (which is nothing but RSS feed) is written to the response stream. Note that use of HttpContext parameter in accessing the intrinsic objects.

Creating ATOM feed generator

Now let's move on to generating ATOM feeds. Add a new class called ATOMFeedGenerator.cs and key in the following code to it:

using System;
using System.Collections.Generic;
using System.Text;
using System.Data;
using System.Data.SqlClient;
using System.Configuration;
using System.Xml;
using System.IO;

namespace FeedHandlerLib
{
public class ATOMFeedGenerator
{
public static string GetATOM()
{
MemoryStream ms = new MemoryStream();
XmlTextWriter writer = new XmlTextWriter(ms, null);
SqlConnection cnn = new SqlConnection(
ConfigurationManager.ConnectionStrings["connstr"].
ConnectionString);
SqlCommand cmd = new SqlCommand("select * from 
articles order by pubdate desc", cnn);
cnn.Open();
SqlDataReader reader = cmd.ExecuteReader();
writer.WriteStartElement("feed");
writer.WriteAttributeString("xmlns", 
"http://www.w3.org/2005/Atom");
writer.WriteElementString("id", 
"www.somedomain.com");
writer.WriteElementString("title", 
"MyWebSite.com");
writer.WriteElementString("link", 
"http://mywebsite.com");
writer.WriteElementString("updated", 
DateTime.Now.ToString("yyyy-MM-ddThh:mm:ssZ"));

writer.WriteStartElement("author");
writer.WriteElementString("name", "Mr.Abcd");
writer.WriteElementString("email", 
"someemail@somedomain.com");
writer.WriteEndElement();

while (reader.Read())
{
writer.WriteStartElement("entry");
writer.WriteElementString("title", 
reader.GetString(reader.GetOrdinal("title")));
writer.WriteElementString("id",
 "www.somedomain.com");
writer.WriteElementString("updated", 
reader.GetDateTime(reader.GetOrdinal("pubdate"))
.ToString("yyyy-MM-ddThh:mm:ssZ"));
writer.WriteElementString("link", 
reader.GetString(reader.GetOrdinal("url")));
writer.WriteElementString("summary", reader
.GetString(reader.GetOrdinal("description")));
writer.WriteEndElement();
}
writer.WriteEndElement();
reader.Close();
cnn.Close();
writer.BaseStream.Flush();
writer.Flush();
ms.Flush();
byte[] data = new byte[ms.Length];
ms.Seek(0, SeekOrigin.Begin);
ms.Read(data, 0, data.Length);
ms.Close();
return ASCIIEncoding.ASCII.GetString(data);
}

}
}

This code is very similar to the one that we wrote previously. However, it generates ATOM feed instead of RSS. The following figure shows a sample ATOM feed generated from this class.

Creating ATOM Handler

Creating ATOM handler is similar to creating RSS handler. Add a class called ATOMHandler to the class library and add the following code to it:

using System;
using System.Collections.Generic;
using System.Text;
using System.Web;

namespace FeedHandlerLib
{
public class ATOMHandler : IHttpHandler
{
public bool IsReusable
{
get
{
return false;
}
}

public void ProcessRequest(HttpContext context)
{
context.Response.ContentType = "text/xml";
string str = ATOMFeedGenerator.GetATOM();
context.Response.Write(str); 
}

}
}

As before the ATOMHandler class implements the IHttpHandler interface. This time the ProcessRequest() method calls GetATOM() method of the ATOMFeedGenerator class and writes it on the response stream.

Creating Feed Handler Factory

Now that we have both the handlers ready, let's create the factory class that instantiates and returns the appropriate handler. Add a class called HandlerFactory and key in the following code to it:

using System;
using System.Collections.Generic;
using System.Text;
using System.Web;
using System.IO;

namespace FeedHandlerLib
{
class HandlerFactory:IHttpHandlerFactory
{
public IHttpHandler GetHandler(HttpContext context, 
string requestType, string url, 
string pathTranslated)
{
string path = context.Request.PhysicalPath;
if (Path.GetExtension(path) == ".rss")
{
return new RSSHandler();
}
if (Path.GetExtension(path) == ".atom")
{
return new ATOMHandler();
}
return null;
}

public void ReleaseHandler(IHttpHandler handler)
{
}

}
}

Here, we created a class called HandlerFactory that implements IHttpHandlerFactory interface. In the GetHandler() method we check the extension of the incoming request. If it is .rss then we return a new instance of RSSHandler class. On the other hand if the extension is .atom then we return a new instance of ATOMHandler class.

Configuring the Handler Factory in Web.config

Now that we have the handlers and factory ready, let's do something to use them in our web site. To do so give reference to the FeedHandlerLib.dll that we just created so that the BIN folder of the web site contains the feed handler component. Open the web.config file of the web site and add the following markup to it:

<httpHandlers>
<add path="*.rss,*.atom" 
type="FeedHandlerLib.HandlerFactory" 
verb="GET" />
</httpHandlers>

The <httpHandlers> tag is used to configure one or more custom HTTP handlers. The path attribute is used to specify one or more the extensions (.rss and .atom in our case) and the type attribute is used to specify the fully qualified name of the class that is intended to handle them. Note that though RSSHandler and ATOMHandler are the actual handlers of the .rss and .atom extensions we have specified the factory class name. The factory will take care to instantiate the correct handler at run time depending on the extension of the request.

Configuring the Handler Factory in IIS

Once we configure the handlers in the web.config ASP.NET understands how to process them. However, IIS has no idea about them and doesn't know who should handle them. So the next step is to configure your web site in IIS. Open the properties dialog of your web site in IIS. On the Directory tab you will find a button called Configuration. Clicking on the Configuration button opens the following dialog:

Further click on Add button to open a dialog as shown below:

In the Executable textbox select aspnet_isapi.dll (you should find it in the installation folder of .NET framework). In the extension textbox enter .rss. In the Verbs section specify the verb as GET. Finally, uncheck the "Check the file exists" checkbox and close the dialog. Repeat the same procedure for .atom extension.

Now IIS knows what to do if someone requests .rss and .atom extensions. IIS will simply forward these requests to ASP.NET which then forwards them to the handler factory (because we configured it in the web.config file).

That's it! You are ready to test our handlers now. Simply run your default web page, key in articles.rss in the address bar and hit enter. You should get the RSS feed in the browser window.

Summary

HTTP handlers are flexible way to handle your custom extensions. The handler factories are intended to instantiate handlers depending on some condition. They help in reducing the overall configuration required for your web site. Using HTTP handlers can save us from unnecessary page level event processing thus improving performance.




Bipin Joshi is a software consultant, trainer, author and a yogi having 21+ years of experience in software development. He conducts online courses in ASP.NET MVC / Core, jQuery, AngularJS, and Design Patterns. He is a published author and has authored or co-authored books for Apress and Wrox press. Having embraced Yoga way of life he also teaches Ajapa Meditation to interested individuals. To know more about him click here.

Get connected : Twitter  Facebook  Google+  LinkedIn

Posted On : 31 Jul 2006



Tags : ASP.NET Components XML