Implementing REST Web Services Using ASP.NET 2.0 HTTP Handlers

A Web service is defined as an endpoint that returns data using standard HTTP mechanisms. Although .NET developers have generally associated Web services with SOAP-based services, you’ve already seen examples of REST services and POX (Plain Old XML) services using WCF. At times, however, you might want to process the raw HTTP request and response, and WCF might not be the best solution. For example, in Windows SharePoint Services 3.0, WCF services aren’t supported through the out-of-the-box application. You might choose to implement HTTP handlers for performance reasons, or perhaps the service you’re providing doesn’t fit well with the WCF programming model. HTTP handlers can be mapped to any URL (including wildcards) through Internet Information Services (IIS) and web.config, which offers flexibility but less abstraction than the WCF model.

An HTTP handler gives you full control over request and response processing and is simply a class that implements the IHttpHandler interface. The HTTP handler has no runtime overhead, making it the most lightweight implementation possible to process a Web request. This is ideal in certain cases, but it does not give you the flexibility or abstraction of the WCF framework. Regardless, an HTTP handler does play a role in service-based AJAX applications and is an interface you should learn about as a Web application developer. The IHttpHandler interface is shown in Example 3-15. It allows you to implement the processing of the request and response through raw access to the current instance of HttpContext.

Example 3-15. The IHttpHandler interface processes the raw HTTP context.

namespace System.Web
{
    public interface IHttpHandler
    {
        bool IsReusable { get; }
        void ProcessRequest(HttpContext context);
    }
}

There are only two methods to implement with the IHttpHandler interface—IsReusable and ProcessRequest.IsReusable specifies that the ASP.NET runtime can reuse the object for multiple requests. It should be set to false for any handler that implements any type of state, such as private fields or properties. If the method doesn’t use state and one instance can be reused for multiple requests, you can gain a performance boost by setting IsReusable to true. ProcessRequest passes in the current HTTP context through the HttpContext class, which gives you full access to the request and the response.

To write a simple HTTP handler, implement the IHttpHandler interface and process the request and response streams in the ProcessRequest method. As you can see in the Hello World HTTP handler example in Example 3-16, implementing the IHttpHandler interface is a trivial task, but it can be a powerful tool for building endpoints because it gives you complete control over the process.

Example 3-16. The IHttpHandler interface is used to process the entire HTTP context (ExampleServices/HelloHandler.cs).

using System;
using System.Web;

namespace ExampleServices
{
    public class HelloHandler : IHttpHandler
    {
        public bool IsReusable { get { return true; } }
        public void ProcessRequest(HttpContext context)
        {
            context.Response.Write("Hello World!");
        }
    }
}

More Information

More Information

If your handler uses any type of asynchronous methods, such as for input/output operations or data access, consider using the IHttpAsyncHandler for asynchronous processing in ASP.NET. For more information about implementing asynchronous handlers, see the MSDN topic "Creating an Asynchronous HTTP Handler" at http://msdn2.microsoft.com/enus/library/ms227433.aspx.

To enable your handler in the Web site, you must register it in web.config in the httpHandlers node. You must register the type and assembly for the handler, as well as the HTTP verb (or multiple verbs, including wildcards) that it will respond to. Here is the web.config entry for our HelloHandler class, using the URL helloworld.aspx.

<httpHandlers>
   <add verb="GET" path="HelloWorld.aspx"
      type="ExampleServices.HelloHandler, ExampleServices" validate="false"/>
</httpHandlers>

Tip

Tip

When registering an HTTP handler, you must register it with a file extension that is handled by the ASP.NET framework. You can use any extension as long as it is mapped to the ASP.NET process in IIS. In Windows SharePoint Services 3.0, all file extensions are configured to be processed by ASP.NET and you will not have this constraint for services running under the SharePoint application.

You can also specify which file extensions are processed by ASP.NET in IIS. In IIS 7.0 you can open the Web site’s Handler Mappings section, where you can specify the executable to handle the request with a script map. To process all requests through the ASP.NET engine, specify the "*" request path mapping and use the path to your ASP.NET DLL, aspnet_isapi.dll.

Instead of registering HTTP handlers in web.config directly, you can map wildcards to an IHttpHandlerFactory interface. For example, you can map all the files in a subdirectory to a handler factory with the following configuration. This web.config file would be deployed in a subdirectory such as "/feeds".

<httpHandlers>                                                                     
   <add verb="*" path="*"                                                          
      type="ExampleServices.FeedHandlerFactory, ExampleServices" validate="false"/>
</httpHandlers>                                                                    

The IHttpHandlerFactory interface is a simple factory interface that enables you to specify which HTTP handler should process a request. If mapping requests to services through configuration isn’t practical because of your hosting environment, the HTTP handler factory can be a great alternative. When it is more practical to use code than configuration to decide which service should handle a URL endpoint, an HTTP handler factory might be your best architectural option.

Example 3-17 shows the IHttpHandlerFactory interface. The GetHandler method uses the current HTTP context as well as physical path information to let your code determine which handler to return.

Example 3-17. The IHttpHandlerFactory interface is used to return an HTTP handler for processing a request.

namespace System.Web
{
    //  Defines the contract for IHttpHandler class factories
    public interface IHttpHandlerFactory
    {
        // Returns an instance of the IHttpHandler interface.
        IHttpHandler GetHandler(HttpContext context,
            string requestType, string url, string pathTranslated);

        // Enables a factory to reuse an existing handler instance.
        void ReleaseHandler(IHttpHandler handler);
    }
}

To implement the handler, inherit from IHttpHandlerFactory and choose which handler to return in the GetHandler method. The UriTemplate class can be useful for parsing URLs and determining which handler to call based on the URI. In the following code samples, I use the UriTemplate class to determine the handler for an endpoint and return a syndication feed using the syndication library from the System.ServiceModel.Web assembly. This is the same endpoint functionality as shown in Example 3-14, but it is expressed through an ASP.NET HTTP handler implementation. Example 3-18 demonstrates the handler factory that uses the UriTemplate to determine the endpoint.

Example 3-18. To implement the IHttpHandlerFactory, implement logic to choose which handler to return based on the path translated, URL requested, and verb (ExampleServices/FeedHandlerFactory.cs).

using System;
using System.Web;

namespace ExampleServices
{
    public class FeedHandlerFactory : IHttpHandlerFactory
    {
        public IHttpHandler GetHandler(HttpContext context,
            string requestType, string url, string pathTranslated)
        {
            // Use a simple UriTemplate to determine the handler to return.
            UriTemplate template = new UriTemplate("feed.aspx/{catalog}");
            Uri baseAddress = new Uri(
                context.Request.Url.GetLeftPart(UriPartial.Authority) +
                context.Request.ApplicationPath);
            UriTemplateMatch results = template.Match(baseAddress,
                context.Request.Url);
            if (results != null)
            {
                string catalog = results.BoundVariables["catalog"];
                return new RecentlyPostedFeedHandler(catalog);
            }

            else
                return new ExampleXmlHandler();
        }

        public void ReleaseHandler(IHttpHandler handler)
        {
            // Use this method to free up any state to enable reuse
            // for the handler instance.
        }
    }
}

The IHttpHandler interface is used to return IHttpHandler implementations, deferring the decision to run time rather than configuration. In this case, I’ve provided a UriTemplate implementation to determine the handler and to parse the service parameters, an approach that is similar to using the WebGet attribute with UriTemplate in WCF technologies. You can see here that the code is very similar to the WCF implementation, although slightly more processing is required. Example 3-19 completes the HTTP handler sample by implementing an Atom feed that uses the same code as previously implemented in WCF.

Example 3-19. To implement IHttpHandler, handle the HTTP context directly (ExampleServices/RecentlyPosted FeedHandler.cs).

using System;
using System.Text;
using System.Web;
using KnowledgeBase;
using System.Xml.Serialization;
using System.ServiceModel.Syndication;
using System.Xml;
using KnowledgeBase.Implementation;
using System.Collections.Generic;

namespace ExampleServices
{
    public class RecentlyPostedFeedHandler : IHttpHandler
    {
        public bool IsReusable
        {
            get { return false; }
        }

        public string Catalog { get; set; }
        public RecentlyPostedFeedHandler(string catalog) { this.Catalog = catalog; }

        public void ProcessRequest(HttpContext context)
        {
            // Creates a new feed using the WCF Syndication library.
            context.Response.ContentType = "text/xml";
            SyndicationFeed feed =
                new SyndicationFeed("Recently Posted: " + this.Catalog,
                "Recently Posted items for " + this.Catalog, null);

            DataAccess knowledgeBase = new DataAccess(HttpContext.Current.User);
            List<SyndicationItem> items =
                knowledgeBase.GetRecentlyPosted( this.Catalog);
            feed.Items = items;
            SyndicationFeedFormatter formatter = new Atom10FeedFormatter(feed);
            XmlWriter outputWriter = new XmlTextWriter(context.Response.Output);
            formatter.WriteTo(outputWriter);
            HttpContext.Current.Response.Cache.SetLastModified(DateTime.UtcNow);
            HttpContext.Current.Response.Cache.SetCacheability(
                HttpCacheability.Private);
            outputWriter.Flush();
        }
    }
}

While less abstracted than WCF services and tightly coupled to the HTTP runtime and ASP.NET, HTTP handlers can play an important part in AJAX service architectures, both for implementing services and for providing AJAX infrastructure components. HTTP handlers are the most lightweight HTTP processing mechanisms available using the .NET Framework.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset