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