HttpModule object and HttpHandler object in ASP.NET

Before we talk about HttpModule objects and HttpHandler objects, let's first understand how ASP.NET works.


When a request is sent to and accepted by the server IIS, the IIS server will look for the file and send it to the client automatically, depending on the file type requested, if it is a static file.

If an aspx page is requested, IIS passes through aspnet_isapi.dll hands over the request to ASP.NET Runtime for processing, and the final result is returned to IIS and sent to the client.

In ASP.NET Runtime, processing requests is done through a series of objects, including HttpApplication, HttpModule, and HttpHandler.

HTTP Module Object

Simply put, the HttpModule object is the "gateway" to HTTP requests, and it can act as a filter by performing additional operations on the request before it reaches the HttpHandler, or in some cases terminating qualified HTTP requests.

As you can see from the diagram of the ASP.NET operating mechanism, there can be multiple HttpModule objects, each HTTP request will go through each HttpModule one by one.

After talking about the basic theory of HttpModule, we can implement an HttpModule object by ourselves. We only need to inherit the IHttpModule interface and implement the two methods below to create an HttpModule object of our own.

// MeHttpModule.cs

// Inherit IHttpModule interface
public class MeHttpModule : IHttpModule 
{
	// Implement Init and Dispose methods
	public void Init(HttpApplication context) 
	{
		// Bind two events for the HttpApplication object
		// Event triggered before HttpHandler request processing
		context.BeginRequest += Context_BeginRequest;
		// Events triggered after the request has been processed
		context.EndRequest += Context_EndRequest;
	}
	
	public void Dispose()
	{
		throw new NotImplementedException();
	}

	// Prior to request processing
	private void Context_BeginRequest(object sender, EventArgs e)
	{
		HttpApplication application = sender as HttpApplication();
		application.Response.Write("<p>HttpModule Start processing request</p>");
	}

	// After the request has been processed
	private void Context_EndRequest(object sender, EventArgs e)
	{
		HttpApplication application = sender as HttpApplication();
		application.Response.Write("<p>HttpModule End processing request</p>");
	}
}

The above is the HttpModule class we implemented ourselves, and then we need to configure it in web.config to add it under the <configuration>node:

<system.webServer>
	<modules>
		<add name="testModule" type="Web.MeHttpModule" />
	</modules>
</system.webServer>

Finally, we create two ASP.NET pages, add <h2>first page </h2>, <h2>second page </h2> in the <body>tab, run the program and visit the first page:

Visit the second page:

When accessing an aspx page, ASP.NET creates an instance of the HttpApplication class, as well as a HttpModule instance registered in the web.config. When creating an HttpModule instance, it calls the Init() method, binding the BeginRequest and EndReques event handling methods of the HttpApplication object

HttpHandler object

From the above, you know that each HTTP request may be processed by multiple HttpModules, but the final processing is in the HttpHandler object. If HttpModule is responsible for the initialization of all requests and the end of requests, then HttpHandler is responsible for the specific work.

As you can see from the ASP.NET Operating Mechanism diagram, an HTTP request is ultimately handled by the ProcessRequest() method of the HttpHandler object

To implement our own HttpHandler object, we need to implement the IHttpHandler interface to implement the ProcessRequest() method and the IsReusable property.

// MeHttpHandler.cs

// Inherit IHttpHandler interface
public class MeHttpHandler : IHttpHandler
{
	// Implement IsReusable property and ProcessRequest method
	public bool IsReusable
	{
		// Is the HttpHandler reusable
		get { return false }
	}

	public void ProcessRequest(HttpContext context)
	{
		context.Response.ContentType = "text/html";
		context.Response.Write("Hello World");
	}
}

Like HttpModule, we need to configure it in web.config after implementation

<system.webServer>
	<handlers>
		<add verb="*" path="Admin/*.aspx" name="meHandler" type="Web.MeHttpHandler" />
	</handlers>
</system.webServer>

The meanings of the attributes are as follows:

  • verb: Represents the type of request (get, post, etc.), * is a wildcard character, representing all requests
  • Path: Access Path means that whenever the file specified by this attribute is requested, it is handled by the HttpHandler class specified by the type attribute. Here we configure that all interfaces under the Admin path will be handled by the MeHttpHandler class as long as we access them
  • type: Assembly or class, ASP.NET will first search in the application's proprietary Bin directory, and if not, continue searching the system assembly cache.

Create a new Admin folder in the Web site, add an Index.aspx page under the folder, and add the following:

<h2>Administrator interface</h2>

Request the Index.aspx page under Admin:


Normally, the administrator interface should be displayed, but we have configured HttpModule and HttpHandler ourselves, so what is displayed is what is in HttpModule and HttpHandler.

actual combat

With that much theoretical knowledge, we will use its mechanism to implement anti-theft chains and watermarks for pictures.

Anti-theft chain

Anti-theft Chain Requirements

  • Create a website with a picture folder under it. All pages of this site can access picture resources normally
  • Create a website where all pages below the site refer to pictures from the site above, and as a result, pictures will not display properly

First we create the first website, then add a page and insert three pictures:

<img src="./Images/adv1.png" />
<img src="./Images/adv2.png" />
<img src="./Images/adv3.png" />

Preview interface:


In creating a chain-stealing site, add a page and reference three pictures from the first site:

<p>The following sources come from the first website</p>
<img src="http://localhost:11370/Images/adv1.png" />
<img src="http://localhost:11370/Images/adv2.png" />
<img src="http://localhost:11370/Images/adv3.png" />

Preview interface:


Obviously, the second website stole the first website's picture resources, and then we solved the theft chain through HttpHandler.

Add the HttpHandler class under the first website

public class PreventLink : IHttpHandler
{
	public bool IsReusable
	{
		get { return true; }
	}
	
	public void ProcessRequest(HttpContext context)
	{
		// Get the URL of the last request
		Uri lastUrl = context.Request.UrlReferrer;
		// Get the URL of this request
		Uri currentUrl = context.Request.Url;

		// Determine whether the chain is stolen
		if (lastUrl.Host != currentUrl.Host || lastUrl.Port != currentUrl.Port) 
		{
			// Get the "Don't steal chains" warning prompt picture
			string errorImagePath = context.Request.PhysicalApplicationPath + "Error/default.png";
			// Send pictures to client 
			context.Response.WriteFile(errorImagePath);
		}
		else
		{
			// Send the correct picture to the client
			context.Response.WriteFile(context.Request.PhysicalPath);
		}
	}
}

Configure in web.config after writing:

<system.webServer>
	<handlers>
		<add verb="*" path="Images/*.png" name="images" type="_531word.TestHandler" />
	</handlers>
</system.webServer>

Preview the first page:


Preview the second page:

If you do not change after encountering the configuration, turn off the browser's resource cache:

Picture Watermark

Above we used HttpHandler to implement anti-theft chain function, and then we continue to implement watermarking for pictures:

Create a website and add pages with the following code:

<img src="./Images/image1.png" />
<img src="./Images/image2.png" />
<img src="./Images/image333.png" />

Preview effect:

There is currently no watermark and the third picture is not visible. Next, add the HttpHandler class to the website:

public class TestHandler : IHttpHandler
{
	public bool IsReusable { get { return false; } }

	// Watermark Picture
	private const string WATERMARK_URL = "~/Images/watermark.png";

	// Picture does not exist. Picture displayed by default
	private const string DEFAULTIMAGE = "~/Images/default.png";

	public void ProcessRequest(HttpContext context)
	{
		// Get the picture requested by the user
		string filePath = context.Request.PhysicalPath; // Request Path
		Image Cover;
		// Determine whether the requested physical file exists
		if (File.Exists(filePath))
        {
        	// load file
        	Cover = Image.FromFile(filePath);
        	// Load Watermark Picture
        	Image watermark = Image.FromFile(context.Server.MapPath(WATERMARK_URL));
        	// Instantiate Canvas
        	Graphics g = Graphics.FromImage(Cover);
        	// Draw watermarks on image s
        	g.DrawImage(watermark, new Rectangle(Cover.Width - watermark.Width,
        		Cover.Height - watermark.Height, watermark.Width, watermark.Height), 0,
        		0, watermark.Width, watermark.Height, GraphicsUnit.Pixel);
        	// Release Canvas
        	g.Dispose();
        	// Release Watermark
        	g.Dispose();
        }
        else
        {
        	// Load default pictures
        	Cover = Image.FromFile(context.Request.MapPath(DEFAULTIMAGE));
        }

		// Format Output
		context.Response.ContentType = "image/jpeg";
		// Save Pictures in Output Stream
		Cover.Save(context.Response.OutputStream, ImageFormat.Jpeg);
		Cover.Dispose();
		context.Response.End();
	}
}

Configure in web.config after writing:

<system.webServer>
	<handlers>
		<add verb="*" path="Images/*.png" name="images" type="_531word.TestHandler" />
	</handlers>
</system.webServer>

Preview again:

The watermark is added, and pictures that do not exist on the server side can also be displayed by default

Tags: ASP.NET

Posted by Benny007 on Tue, 01 Jun 2021 02:19:07 +0930