Introduction
In this article I have elaborated how to restrict file downloading from a website using
HTTP handlers. There are alternate ways to restrict file downloading, e.g., using
Forms authentication. It depends upon your solution and the architecture that best suits you. So in this article
I am using the HTTP handler approach for restricting file downloads if a user
session doesn't exist.
Background
HTTP Handlers:
(MSDN definition)
An ASP.NET HTTP handler is the process (frequently referred to as the "endpoint") that runs in response to a request made to an ASP.NET Web application.
So HTTP handlers are extension based processors, for example, in IIS server
an HTTP handler which entertains the request for a .aspx extension is ISSAPI.dll (in classic mode).
We can also create custom handlers for required extensions (.pdf, .jpg,
.doc) by implementing the
IHttpHandler
class and registering in the handler section of IIS server. So
come and have a trip with me to handlers so that
I can tell you about how to create, implement, and register HTTP handlers.
How to create an HTTP handler
There are two ways to create HTTP handlers:
1) To create
an HTTP handler create a C# class file and inherit from the IHttpHandler
interface, implement both the method and property
below.
public void ProcessRequest(System.Web.HttpContext context)
{
}
public bool IsReusable
{
get
{
return true;
}
}
2) Right click on the solution in the Solution Explorer tab and then select
Add New Item and select Generic Handler from the template.
This will create a .ashx file that is basically nothing but a wrapper of
the class file that has implemented the IHttpHandler
interface.
In my example I will use the first approach.
If you are new to HTTP handlers then the following links will provide you with
the basics concepts:
Using the code
A little elaboration about the attached demo website follows:
Pages: I have created a simple web site in ASP.NET web forms. I have a login.aspx page, LogedInIndex.aspx page, and
an UnloggedInIndex.aspx page. I have hard coded the User ID
and the password for the login. (User ID: Tanzeelur, Password: Rehman). As it is just a demo
there is no need to implement a good UI layout, enterprise level logic,
and securities on the login page .
The pages LoggedInIndex.aspx and UnloggedInIndex.aspx both have the same links to
the same files (pdf, docx, xlsx). In the UnloggedInIndex.aspx page you can see the links to the files but cannot download, because when you
request for the file, an HTTP Handler will interrupt and check for the session; if the session doesn't exist then it will redirect to
the login.aspx page, otherwise it will allow you to download the file. On the other hand, in the
LoggedInIndex.aspx page when a user is logged in properly in the login.aspx page,
a session will be created and the page will be redirected to LoggedInIndex.aspx (so here file downloading will be accessible because
the session exists).
C# class files: I have two class files in my App_Code folder: TestingHandler.cs and MIMEType.cs.
TestingHandler.cs: The file TestingHandler.cs is basically the Handler that has implemented
IHttpHandler
(an interface, which a class must inherit to be an HTTPHandler).
TestingHandler.cs has also implemented ISessionReadOnlyState
, without this
the
ISessionReadOnlyState
interface implemented Session object in the handler will always return
null
, due to which an exception will be raised.
Testing handler code is implemented below:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.SessionState;
public class TestingHandler:IHttpHandler,IReadOnlySessionState
{
bool IHttpHandler.IsReusable
{
get { return false; }
}
void IHttpHandler.ProcessRequest(HttpContext context)
{
if(context.Session["User"]==null)
context.Response.Redirect("~/login.aspx");
var filExtension = GettingExtension(context.Request.RawUrl);
context.Response.ClearContent();
context.Response.ClearHeaders();
context.Response.ContentType = MIMEType.Get(filExtension);
context.Response.AddHeader("Content-Disposition", "attachment");
context.Response.WriteFile(context.Request.RawUrl);
context.Response.Flush();
}
public string GettingExtension(string rawUrl)
{
return rawUrl.Substring(rawUrl.LastIndexOf(".", System.StringComparison.Ordinal));
}
}
Let's have a glance over the above code. As I have mentioned in the beginning of this article, to
an create HttpHandler we have to implement
IHttpHandler
. The IsReusable
property and the ProcessRequest
method belong to the IHttpHandler
interface. Your logic always go in the ProcessRequset
method of the handler. In the ProcessRequest
method I am checking
for session["User"]
; when it is null, I am redirecting
it to login.aspx. When the user Session exists, it allows the file to
be downloaded to the client side.
In the Handler I have used the MIMEType.Get(filExtension)
statement,
which I will discuss below.
MIMEType.cs (Mime Type information on
Wikipedia)
This class has a readonly Dictionary<String, String> MimeTypeDict
property having most MimeTypes of file extensions.
The static function Get(filExtension)
returns the MIME type based on the extension of the file requested.
Here I will thank Mr. Sameul Neff for posting this on StackOverFlow.
Some of the code from MIMEtype.cs is listed below:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
public class MIMEType
{
#region MIME type list
private static readonly Dictionary<String, String> MimeTypeDict = new Dictionary<String, String>()
{
{ "pdf", "application/pdf" },
{ "xlsx", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" },
{ "docx", "application/vnd.openxmlformats-officedocument.wordprocessingml.document" }
};
#endregion
#region Get
public static String Get(String extension)
{
return Get(extension, MimeTypeDict["bin"]);
}
public static String Get(String extension, String defaultMimeType)
{
if (extension.StartsWith("."))
extension = extension.Remove(0, 1);
if (MimeTypeDict.ContainsKey(extension))
return MimeTypeDict[extension];
else
return defaultMimeType;
}
#endregion Get
}
How to register your Handler in IIS
Now it is time to register your handler in IIS, there are two ways:
- Using the interface of IIS. Click on your site -> in the IIS section,
open
Handler Mappings -> in the Action pane at the right most
side of the interface -> click
Add Managed Handler -> Add your handler as shown below.
By doing this it will add the handler in the
web.config file.
- The second method is by adding your handler in the
handlers
section of
the web.config file. I have added mine in the web.config as shown below.
<system.webServer>
<handlers>
<add name="FileXlsx" path="*.xlsx" verb="*"
type="TestingHandler" resourceType="File" preCondition="integratedMode"/>
<add name="Filedocx" path="*.docx" verb="*"
type="TestingHandler" resourceType="File" preCondition="integratedMode"/>
<add name="FilePdf" path="*.pdf" verb="*"
type="TestingHandler" resourceType="File" preCondition="integratedMode"/>
</handlers>
</system.webServer>
That's all! I have added the demo application with the article. Feel free to query if there are any issues regarding the article or the attached demo application.