Monday, July 14, 2008

Creating a simple HttpHandlers

This is a simple post to demonstrate how to create a simple httphandler to download a pdf (or any excel / doc etc).

This might coincide with many other blogs in contents because I had this learning from many other websites (BoilerPlate is one among).

Lets start...

Step 1:
Create a project (C#/VB) and add a class (some thing like HttpHandlerBase.cs) that inherits from IHttpHandler

Step 2:
Make sure you implement the method ProcessRequest

Step 3:
Make sure you implement the property IsReusable

Step 4:
This class can be made abstract on need basis

Step 5:
If not step 4, implement your code for downloading the PDF. Else, from another class inherit the above class and override the processrequest method and write your implementation.

Step 6:
Inside Web.Config, under httpHandlers section add verb
<add verb="GET" path="DownloadPdf.ashx" type="Assembly Information, NameSpace" validate="false" />

Step 7:
from any page you want to call, just call the link as below:
<asp:HyperLink ID="link1" runat="server" NavigateUrl="DownloadPdf.ashx"/>

Note the DownloadPdf.ashx, i am following the same path name. Incase you want any .ashx, replace the verb path to path="*.ashx" to divert all the calls to the above mentioned assembly.
//____________________________________________________________
//Code
//------------------------------------------------------------
// HTTPHANDLER BASE CLASS
//------------------------------------------------------------
using System;
using System.Net;
using System.Web;

// This Abstract class is taken from
// http://haacked.com/archive/2005/03/17/AnAbstractBoilerplateHttpHandler.aspx
namespace CodeName.Srini.HttpHandlers
{
/// 
/// An abstract base Http Handler for all your
/// needs.
/// 
/// 
/// 
/// For the most part, classes that inherit from this
/// class do not need to override .
/// Instead implement the abstract methods and
/// properties and put the main business logic
/// in the .
/// 
/// 
/// HandleRequest should respond with a StatusCode of
/// 200 if everything goes well, otherwise use one of
/// the various "Respond" methods to generate an appropriate
/// response code. Or use the HttpStatusCode enumeration
/// if none of these apply.
/// 
/// 
public abstract class HttpHandlerBase : IHttpHandler
{
public HttpHandlerBase()
{
// Default Constructor
}

/// 
/// Processs the incoming HTTP request.
/// 
/// 
Context.public void ProcessRequest(HttpContext context)
{
SetResponseCachePolicy(context.Response.Cache);

if (!ValidateParameters(context))
{
RespondInternalError(context);
return;
}

if (RequiresAuthentication
&& !context.User.Identity.IsAuthenticated)
{
RespondForbidden(context);
return;
}

context.Response.ContentType = ContentMimeType;

HandleRequest(context);
}

/// 
/// Indicates whether or not this handler can be
/// reused between successive requests.
/// 
/// 
/// Return true if this handler does not maintain
/// any state (generally a good practice). Otherwise
/// returns false.
/// 
public bool IsReusable
{
get
{
return true;
}
}

/// 
/// Sets the cache policy. Unless a handler overrides
/// this method, handlers will not allow a respons to be
/// cached.
/// 
/// 
Cache.public virtual void SetResponseCachePolicy(HttpCachePolicy cache)
{
cache.SetCacheability(HttpCacheability.NoCache);
cache.SetNoStore();
cache.SetExpires(DateTime.MinValue);
}

/// 
/// Validates the parameters. Inheriting classes must
/// implement this and return true if the parameters are
/// valid, otherwise false.
/// 
/// 
Context./// true if the parameters are valid,
/// otherwise false
public abstract bool ValidateParameters(HttpContext context);

/// 
/// Helper method used to Respond to the request
/// that an error occurred in processing the request.
/// 
/// 
Context.protected void RespondInternalError(HttpContext context)
{
// It's really too bad that StatusCode property
// is not of type HttpStatusCode.
context.Response.StatusCode =
(int)HttpStatusCode.InternalServerError;
context.Response.End();
}

/// 
/// Gets a value indicating whether this handler
/// requires users to be authenticated.
/// 
/// 
/// true if authentication is required
/// otherwise, false.
/// 
public abstract bool RequiresAuthentication { get; }

/// 
/// Helper method used to Respond to the request
/// that the request in attempting to access a resource
/// that the user does not have access to.
/// 
/// 
Context.protected void RespondForbidden(HttpContext context)
{
context.Response.StatusCode
= (int)HttpStatusCode.Forbidden;
context.Response.End();
}

/// 
/// Gets the content MIME type.
/// 
/// 
public abstract string ContentMimeType { get; }

/// 
/// Handles the request. This is where you put your
/// business logic.
/// 
/// 
/// 
This method should result in a call to one
/// (or more) of the following methods:
/// 
context.Response.BinaryWrite();
/// 
context.Response.Write();
/// 
context.Response.WriteFile();
/// 
/// 
/// someStream.Save(context.Response.OutputStream);
/// 
/// 
/// 
etc...
/// 
/// If you want a download box to show up with a
/// pre-populated filename, add this call here
/// (supplying a real filename).
/// 
/// 
/// 
/// Response.AddHeader("Content-Disposition"
/// , "attachment; filename=\"" + Filename + "\"");
/// 
/// 
/// 
Context.public abstract void HandleRequest(HttpContext context);
}
}

//------------------------------------------------------------
// DERIVED CLASS
//------------------------------------------------------------

using System;
using System.Data;
using System.Data.SqlClient;
using System.Web;

namespace CodeName.Srini.HttpHandlers
{
public class DownloadPublishedReport : HttpHandlerBase
{
private const string NoDataMessage = "No downloadable pdf file available for this record.";

public override bool ValidateParameters(HttpContext context)
{
return true;
}

public override bool RequiresAuthentication
{
get { return false; }
}

public override void HandleRequest(HttpContext context)
{
DownloadFileFromServer(context);
}

public override string ContentMimeType
{
get { return "application/pdf"; }
}

private void DownloadFileFromServer(HttpContext context)
{
string pdfFileName = string.Empty;
Byte[] pdfData = null;

// CREATE UR OWN DATAVIEW TO GET THE BYTE CODE AND THE FILE NAME FOR PDF
if (dv != null)
{
if (dv.Table.Rows.Count > 0)
{
DataRow dr = dv.Table.Rows[0];

pdfFileName = dr[1].ToString();
pdfData = (byte[])(dr[4]);
}
}

if (pdfData != null)
{
BuildPdfAndDownload(context, pdfData, pdfFileName);
}
else
{
byte[] buffer = new byte[NoDataMessage.Length];

System.Text.ASCIIEncoding encoding = new System.Text.ASCIIEncoding();
buffer = encoding.GetBytes(NoDataMessage);

BuildPdfAndDownload(context, buffer, "NoData.pdf");
}
}

private void BuildPdfAndDownload(HttpContext context, Byte[] pdfData, string pdfFileName)
{
context.Response.Buffer = true;
context.Response.Clear();
context.Response.ContentType = "application/octet-stream";

context.Response.AddHeader("content-disposition", "attachment;filename=" + pdfFileName);
context.Response.AddHeader("content-length", pdfData.Length.ToString());
context.Response.BinaryWrite(pdfData);

context.Response.End();
}
}
}

//------------------------------------------------------------
// WEB.CONFIG SETTINGS
//------------------------------------------------------------

<httpHandlers>
<add verb="GET" path="DownloadPdf.ashx" type="CodeName.Srini.HttpHandlers.DownloadPublishedReport, CodeName.Srini.HttpHandlers" validate="false" />
</httpHandlers>

//------------------------------------------------------------
// CALLING ASPX PAGE
//------------------------------------------------------------

<asp:HyperLink ID="link1" runat="server" NavigateUrl="DownloadPdf.ashx" />