Asp.net 处理程序&处理程序接口IHttpHandler

通过Asp.Net构架(Http请求处理流程)HttpApplication处理对象与HttpModule处理模块前面两篇我们了解了Http请求在服务器端的处理流程,Http请求最终会由实现了IHttpHandler接口的类进行处理,针对不同的请求,Asp.net要有不同的处理。通常情况下,HTTP.SYS根据请求的扩展名来确定ISAPI处理程序,再通过各种处理程序来分别进行处理。

回顾一下Http请求处理流程

  • 当Http请求进入 Asp.Net Runtime以后,它的管道由托管模块(NOTE:Managed Modules)和处理程序(NOTE:Handlers)组成,并且由管道来处理这个 Http请求
  • HttpRuntime将Http请求转交给 HttpApplication,HttpApplication代表着程序员创建的Web应用程序。HttpApplication创建针对此Http请求的 HttpContext对象,这些对象包含了关于此请求的诸多其他对象,主要是HttpRequest、HttpResponse、HttpSessionState等
  • 接下来Http请求通过一系列Module,这些Module对Http请求具有完全的控制权。这些Module可以做一些执行某个实际工作前的事情。
  • Http请求经过所有的Module之后,它会被HttpHandler处理。
  • HttpHandler处理完以后,Http请求再一次回到Module,此时Module可以做一些某个工作已经完成了之后的事情。

HttpModule源码

//------------------------------------------------------------
// Copyright (c) Microsoft Corporation.  All rights reserved.
//------------------------------------------------------------
namespace System.ServiceModel.Activation
{
    using System.Diagnostics;
    using System.Runtime;
    using System.Security;
    using System.ServiceModel;
    using System.Web;
    using System.Web.Hosting;

    class HttpModule : IHttpModule
    {
        static bool disabled;

        [Fx.Tag.SecurityNote(Miscellaneous = "RequiresReview - called outside PermitOnly context.")]
        public void Dispose()
        {
        }

        [Fx.Tag.SecurityNote(Critical = "Entry-point from asp.net, accesses ProcessRequest which is SecurityCritical.")]
        [SecurityCritical]
        public void Init(HttpApplication context)
        {
            context.PostAuthenticateRequest += new EventHandler(ProcessRequest);
        }

        [Fx.Tag.SecurityNote(Critical = "Entry-point from asp.net, called outside PermitOnly context. ASP calls are critical." +
            "HostedHttpRequestAsyncResult..ctor is critical because it captures HostedImpersonationContext." +
            "(and makes it available later) so caller must ensure that this is called in the right place.")]
        [SecurityCritical]
        static void ProcessRequest(object sender, EventArgs e)
        {
            if (HttpModule.disabled)
            {
                return;
            }

            try
            {
                ServiceHostingEnvironment.SafeEnsureInitialized();
            }
            catch (SecurityException exception)
            {
                HttpModule.disabled = true;

                DiagnosticUtility.TraceHandledException(exception, TraceEventType.Warning);

                // If requesting a .svc file, the HttpHandler will try to handle it.  It will call
                // SafeEnsureInitialized() again, which will fail with the same exception (it is
                // idempotent on failure).  This is the correct behavior.
                return;
            }

            HttpApplication application = (HttpApplication)sender;

            // Check to see whether the extension is supported
            string extension = application.Request.CurrentExecutionFilePathExtension;
            if (string.IsNullOrEmpty(extension))
            {
                return;
            }

            ServiceHostingEnvironment.ServiceType serviceType = ServiceHostingEnvironment.GetServiceType(extension);
            // do extension check first so that we do not need to do it in aspnetrouting/configurationbasedactivation
            if (serviceType == ServiceHostingEnvironment.ServiceType.Unknown)
            {
                return;
            }
            
            // check for AspNetcompat
            if (ServiceHostingEnvironment.AspNetCompatibilityEnabled)
            {
                // remap httphandler for xamlx in CBA, since there is No physical file and 
                // the xamlx httphandlerfactory will do file exist checking
                if (serviceType == ServiceHostingEnvironment.ServiceType.Workflow && ServiceHostingEnvironment.IsConfigurationBasedService(application)) 
                {
                    application.Context.RemapHandler(new HttpHandler());                   
                }
                return;
            }

            else if (serviceType == ServiceHostingEnvironment.ServiceType.WCF)
            {
                HostedHttpRequestAsyncResult.ExecuteSynchronous(application, false, false);
            }
            else if (serviceType == ServiceHostingEnvironment.ServiceType.Workflow)
            {
                HostedHttpRequestAsyncResult.ExecuteSynchronous(application, false, true);               
            }
        }
    }
}

HttpHandler源码

//------------------------------------------------------------
// Copyright (c) Microsoft Corporation.  All rights reserved.
//------------------------------------------------------------
namespace System.ServiceModel.Activation
{
    using System.Runtime;
    using System.Security;
    using System.ServiceModel;
    using System.Web;
    using System.Web.SessionState;

    class HttpHandler : IHttpHandler, IRequiresSessionState
    {
        public bool IsReusable
        {
            [Fx.Tag.SecurityNote(Miscellaneous = "RequiresReview - called outside PermitOnly context.")]
            get
            {
                return true;
            }
        }

        [Fx.Tag.SecurityNote(Critical = "Entry-point from asp.net, called outside PermitOnly context.")]
        [SecurityCritical]
        public void ProcessRequest(HttpContext context)
        {
            ServiceHostingEnvironment.SafeEnsureInitialized();

            HostedHttpRequestAsyncResult.ExecuteSynchronous(context.ApplicationInstance, true, false);
        }
    }
}

IHttpHandler

在Asp.net中,所有的处理程序类必须实现IHttpHandler接口或者实现IHttpAsyncHandler接口,一个同步,一个异步。
IHttpHandler的定义如下:

//------------------------------------------------------------------------------
// <copyright file="IHttpHandler.cs" company="Microsoft">
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>                                                                
//------------------------------------------------------------------------------

/*
 * Synchronous Http request handler interface
 * 
 * Copyright (c) 1998 Microsoft Corporation
 */

namespace System.Web {
    
    using System.Security.Permissions;

    /// <devdoc>
    ///    <para>
    ///       Defines the contract that developers must implement to
    ///       synchronously process HTTP web requests. Developers
    ///       implement the ProcessRequest method to provide custom URL execution.
    ///    </para>
    /// </devdoc>
    public interface IHttpHandler {

        /// <devdoc>
        ///    <para>
        ///       Drives web processing execution.
        ///    </para>
        /// </devdoc>
        void ProcessRequest(HttpContext context);   

        /// <devdoc>
        ///    <para>
        ///       Allows an IHTTPHandler instance to indicate at the end of a
        ///       request whether it can be recycled and used for another request.
        ///    </para>
        /// </devdoc>
        bool IsReusable { get; }
    }
}
  • IsReusable属性表示:“当这个处理程序对象在使用之后,是否还可以被缓存起来,在以后的请求处理中再次使用”,这个属性主要用来配合处理程序工厂使用
  • ProcessRequest是IHttpHandler接口的主要方法,接收并通过一个HttpContext类型的请求上下文对象,处理程序可以得到关于处理请求所需的信息。通过HttpContext的Response属性可以得到响应的对象,用以向客户端返回服务器处理的结果。

IHttpAsyncHandler

IHttpAsyncHandler比IHttpHandler增加了两个方法,BeginProcessRequest和EndProcessRequest方法。

//------------------------------------------------------------------------------
// <copyright file="IHttpAsyncHandler.cs" company="Microsoft">
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>                                                                
//------------------------------------------------------------------------------

/*
 * Asynchronous Http request handler interface
 * 
 * Copyright (c) 2000 Microsoft Corporation
 */

namespace System.Web {

    using System.Security.Permissions;

    /// <devdoc>
    ///    <para>When implemented by a class, defines the contract that Http Async Handler objects must
    ///       implement.</para>
    /// </devdoc>
    public interface IHttpAsyncHandler : IHttpHandler {

        /// <devdoc>
        ///    <para>Registers handler for async notification.</para>
        /// </devdoc>
        IAsyncResult BeginProcessRequest(HttpContext context, AsyncCallback cb, Object extraData);

        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>

        void EndProcessRequest(IAsyncResult result);
    }
}

处理程序工厂IHttpHandlerFactory

//------------------------------------------------------------------------------
// <copyright file="IHttpHandlerFactory.cs" company="Microsoft">
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>                                                                
//------------------------------------------------------------------------------

/*
 * Handler factory interface
 */
namespace System.Web {
    
    using System.Security.Permissions;
    /*
     * Handler factory -- gets Handler by requestType,path,file
     */

    /// <devdoc>
    ///    <para>
    ///       Defines the contract that factories must implement to dynamically
    ///       create IHttpHandler instances.
    ///    </para>
    /// </devdoc>
    public interface IHttpHandlerFactory {

        /// <devdoc>
        ///    <para>
        ///       Returns an instance of an IHttpHandler class.
        ///    </para>
        /// </devdoc>
        IHttpHandler GetHandler(HttpContext context, String requestType, String url, String pathTranslated);

        /// <devdoc>
        ///    <para>
        ///       Enables a factory to recycle or re-use an existing handler
        ///       instance.
        ///    </para>
        /// </devdoc>
        void ReleaseHandler(IHttpHandler handler);
    }

    internal interface IHttpHandlerFactory2 : IHttpHandlerFactory {

        /// <devdoc>
        ///    <para>
        ///       Returns an instance of an IHttpHandler class. Works directly with a VirtualPath object
        ///       to avoid unnecessary conversions and creations.
        ///    </para>
        /// </devdoc>
        IHttpHandler GetHandler(HttpContext context, String requestType, VirtualPath virtualPath, String physicalPath);
    }
}
  • GetHandler方法用来通过这个处理程序工厂获取一个处理程序对象
  • ReleaseHandler方法用来释放一个处理程序对象

注册处理程序

每一种处理程序用来处理一类请求,不同的请求类别通过请求的扩展名来进行区分,处理程序与请求之间的匹配关系在网站的配置文件web.config中通过配置参数来进行设置。system.web配置元素的子元素httpHandlers用来配置网站所使用的处理程序。httpHandlers元素可以包含三种子元素:add、remove和clear。

add子元素有三个必选的属性,作用如下:

  • verb通过一个逗号(,)分割的HTTP请求类型列表来表示处理请求的类型
    • eg:GET,POST等;使用星号(*)表示处理所有类型的请求。
  • path通过一个固定的URL路径或者一个使用星号(*)的通配符来匹配请求的URL
    • eg:使用*.aspx表示这个处理请求将处理所有扩展名为aspx的请求。
  • type处理程序的类型名称,或者是处理程序工厂的类型名称,这个类型必须是类型的全名,包含命名空间、程序集(当类放在私有程序集时)。
  • validate为可选的属性,如果设置为false,那么Asp.net在第一次匹配的请求调用之前,将不会试图加载这个类。

在网站应用程序运行的时候,实际得到的配置文件来自于系统的machine.config,系统的web.config和网站自身的web.config合并。在web.config中Asp.net已经预先配置了57中处理程序的映射,还可以通过处理程序接口扩展自定义的处理程序。

系统的web.config路径:C:\Windows\Microsoft.NET\Framework\v4.0.30319\Config\web.config

<httpHandlers>
            <add path="eurl.axd" verb="*" type="System.Web.HttpNotFoundHandler" validate="True"/>
            <add path="trace.axd" verb="*" type="System.Web.Handlers.TraceHandler" validate="True"/>
            <add path="WebResource.axd" verb="GET" type="System.Web.Handlers.AssemblyResourceLoader" validate="True"/>
            <add verb="*" path="*_AppService.axd" type="System.Web.Script.Services.ScriptHandlerFactory, System.Web.Extensions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" validate="False"/>
            <add verb="GET,HEAD" path="ScriptResource.axd" type="System.Web.Handlers.ScriptResourceHandler, System.Web.Extensions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" validate="False"/>
            <add path="*.axd" verb="*" type="System.Web.HttpNotFoundHandler" validate="True"/>
            <add path="*.aspx" verb="*" type="System.Web.UI.PageHandlerFactory" validate="True"/>
            <add path="*.ashx" verb="*" type="System.Web.UI.SimpleHandlerFactory" validate="True"/>
            <add path="*.asmx" verb="*" type="System.Web.Script.Services.ScriptHandlerFactory, System.Web.Extensions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" validate="False"/>
            <add path="*.rem" verb="*" type="System.Runtime.Remoting.Channels.Http.HttpRemotingHandlerFactory, System.Runtime.Remoting, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" validate="False"/>
            <add path="*.soap" verb="*" type="System.Runtime.Remoting.Channels.Http.HttpRemotingHandlerFactory, System.Runtime.Remoting, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" validate="False"/>
            <add path="*.asax" verb="*" type="System.Web.HttpForbiddenHandler" validate="True"/>
            <add path="*.ascx" verb="*" type="System.Web.HttpForbiddenHandler" validate="True"/>
            <add path="*.master" verb="*" type="System.Web.HttpForbiddenHandler" validate="True"/>
            <add path="*.skin" verb="*" type="System.Web.HttpForbiddenHandler" validate="True"/>
            <add path="*.browser" verb="*" type="System.Web.HttpForbiddenHandler" validate="True"/>
            <add path="*.sitemap" verb="*" type="System.Web.HttpForbiddenHandler" validate="True"/>
            <add path="*.dll.config" verb="GET,HEAD" type="System.Web.StaticFileHandler" validate="True"/>
            <add path="*.exe.config" verb="GET,HEAD" type="System.Web.StaticFileHandler" validate="True"/>
            <add path="*.config" verb="*" type="System.Web.HttpForbiddenHandler" validate="True"/>
            <add path="*.cs" verb="*" type="System.Web.HttpForbiddenHandler" validate="True"/>
            <add path="*.csproj" verb="*" type="System.Web.HttpForbiddenHandler" validate="True"/>
            <add path="*.vb" verb="*" type="System.Web.HttpForbiddenHandler" validate="True"/>
            <add path="*.vbproj" verb="*" type="System.Web.HttpForbiddenHandler" validate="True"/>
            <add path="*.webinfo" verb="*" type="System.Web.HttpForbiddenHandler" validate="True"/>
            <add path="*.licx" verb="*" type="System.Web.HttpForbiddenHandler" validate="True"/>
            <add path="*.resx" verb="*" type="System.Web.HttpForbiddenHandler" validate="True"/>
            <add path="*.resources" verb="*" type="System.Web.HttpForbiddenHandler" validate="True"/>
            <add path="*.mdb" verb="*" type="System.Web.HttpForbiddenHandler" validate="True"/>
            <add path="*.vjsproj" verb="*" type="System.Web.HttpForbiddenHandler" validate="True"/>
            <add path="*.java" verb="*" type="System.Web.HttpForbiddenHandler" validate="True"/>
            <add path="*.jsl" verb="*" type="System.Web.HttpForbiddenHandler" validate="True"/>
            <add path="*.ldb" verb="*" type="System.Web.HttpForbiddenHandler" validate="True"/>
            <add path="*.ad" verb="*" type="System.Web.HttpForbiddenHandler" validate="True"/>
            <add path="*.dd" verb="*" type="System.Web.HttpForbiddenHandler" validate="True"/>
            <add path="*.ldd" verb="*" type="System.Web.HttpForbiddenHandler" validate="True"/>
            <add path="*.sd" verb="*" type="System.Web.HttpForbiddenHandler" validate="True"/>
            <add path="*.cd" verb="*" type="System.Web.HttpForbiddenHandler" validate="True"/>
            <add path="*.adprototype" verb="*" type="System.Web.HttpForbiddenHandler" validate="True"/>
            <add path="*.lddprototype" verb="*" type="System.Web.HttpForbiddenHandler" validate="True"/>
            <add path="*.sdm" verb="*" type="System.Web.HttpForbiddenHandler" validate="True"/>
            <add path="*.sdmDocument" verb="*" type="System.Web.HttpForbiddenHandler" validate="True"/>
            <add path="*.mdf" verb="*" type="System.Web.HttpForbiddenHandler" validate="True"/>
            <add path="*.ldf" verb="*" type="System.Web.HttpForbiddenHandler" validate="True"/>
            <add path="*.exclude" verb="*" type="System.Web.HttpForbiddenHandler" validate="True"/>
            <add path="*.refresh" verb="*" type="System.Web.HttpForbiddenHandler" validate="True"/>
            <add path="*.svc" verb="*" type="System.ServiceModel.Activation.HttpHandler, System.ServiceModel.Activation, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" validate="False"/>
            <add path="*.rules" verb="*" type="System.Web.HttpForbiddenHandler" validate="True"/>
            <add path="*.xoml" verb="*" type="System.ServiceModel.Activation.HttpHandler, System.ServiceModel.Activation, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" validate="False"/>
            <add path="*.xamlx" verb="*" type="System.Xaml.Hosting.XamlHttpHandlerFactory, System.Xaml.Hosting, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" validate="False"/>
            <add path="*.aspq" verb="*" type="System.Web.HttpForbiddenHandler" validate="True"/>
            <add path="*.cshtm" verb="*" type="System.Web.HttpForbiddenHandler" validate="True"/>
            <add path="*.cshtml" verb="*" type="System.Web.HttpForbiddenHandler" validate="True"/>
            <add path="*.vbhtm" verb="*" type="System.Web.HttpForbiddenHandler" validate="True"/>
            <add path="*.vbhtml" verb="*" type="System.Web.HttpForbiddenHandler" validate="True"/>
            <add path="*" verb="GET,HEAD,POST" type="System.Web.DefaultHttpHandler" validate="True"/>
            <add path="*" verb="*" type="System.Web.HttpMethodNotAllowedHandler" validate="True"/>
        </httpHandlers>

以上是系统默认的处理程序映射

自定义处理程序

这个类定义在命名空间TestMeb.Utility下,名为ValidateCodeHandler,实现IHttpHandler接口。为了在处理程序中使用Session状态管理,同时实现IRequiresSessionState接口。这个类定义在私有程序集TestMeb.dll中,用来处理GET类型的请求,请求的扩展名为vc,那么在网站项目的配置文件web.config中注册这个处理程序,配置参数如下

<system.webServer>
    <handlers>
      <add name="ValidateCode" verb="GET" path="*.vc" type="TestMeb.Utility.ValidateCodeHandler,TestMeb"/>
    </handlers>
  </system.webServer>

新建一个Asp.net Web程序:

using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.Linq;
using System.Web;
using System.Web.SessionState;

namespace TestMeb.Utility
{
    public class ValidateCodeHandler : IHttpHandler, IRequiresSessionState
    {
        private static Random random = new Random();

        public void ProcessRequest(HttpContext context)
        {
            context.Response.ContentType = "image/jpeg";

            Image image = new Bitmap(60, 30);

            //生成随机数
            int code = random.Next(1000, 10000);
            string codeString = code.ToString();

            //使用会话状态
            context.Session["Code"] = codeString;

            using (Graphics g = Graphics.FromImage(image))
            {
                g.Clear(Color.WhiteSmoke);
                StringFormat sf = new StringFormat();
                sf.Alignment = StringAlignment.Center;
                sf.LineAlignment = StringAlignment.Center;
                g.DrawString(codeString, new Font("Arial", 14), Brushes.Blue, new RectangleF(0, 0, image.Width, image.Height), sf);
            }

            context.Response.ContentType = "image/jpeg";
            image.Save(context.Response.OutputStream, ImageFormat.Jpeg);
        }

        public bool IsReusable
        {
            get { return false; }
        }
    }
}

因为项目是在MVC中建立的,因此需要在MVC路由中添加忽略,避免程序走MVC流程,
在RouteConfig中添加

routes.IgnoreRoute("Remote/{*pathInfo}");

忽略网站根目录下Remote的请求,启动项目,随便打开一个本系统下.vc后缀的路径:

本文参考文档:

posted @ 2020-10-14 09:57  德乌姆列特  阅读(206)  评论(0编辑  收藏  举报