httpHandlers与Http处理程序

版权声明:此文章转载自_infocool

原文链接:http://www.infocool.net/kb/ASP/201609/193520.html

如需转载请联系听云College团队成员小尹 邮箱:yinhy#tingyun.com

ASP.NET HTTP 处理程序是响应对 ASP.NET Web 应用程序的请求而运行的过程(通常称为"终结点")。最常用的处理程序是处理 .aspx 文件的 ASP.NET 页处理程序。用户请求 .aspx 文件时,页通过页处理程序来处理请求。

ASP.NET 页处理程序仅仅是一种类型的处理程序。ASP.NET 还包括其他几种内置的处理程序,例如用于 .asmx 文件的 Web 服务处理程序。

如果您需要进行特殊处理(可以在应用程序中使用文件扩展名进行标识),可以创建自定义 HTTP 处理程序。例如,下面的方案就很好地利用了自定义 HTTP 处理程序:

RSS源 若要为站点创建 RSS 源,可以创建一个可发出 RSS 格式 XML 的处理程序。然后将您应用程序中的 .rss 扩展名(举例)绑定到此自定义处理程序。当用户向站点发送以 .rss 结尾的请求时,ASP.NET 将调用您的处理程序来处理请求。

图像服务器 如果希望 Web 应用程序能够提供不同大小的图像,可以编写一个自定义处理程序来调整图像大小,然后将调整后的图像作为处理程序的响应返回给用户。

HTTP 处理程序可以访问应用程序上下文,包括请求用户的标识(如果已知)、应用程序状态和会话信息等。当请求 HTTP 处理程序时,ASP.NET 将调用相应处理程序上的 ProcessRequest 方法。处理程序的ProcessRequest方法创建一个响应,此响应随后发送回请求浏览器。就像任何页请求那样,响应将途经订阅了处理程序运行后所发生事件的所有 HTTP 模块。有关处理 Web 服务器请求的更多信息,请参见 ASP.NET 应用程序生命周期概述。

HTTP 处理程序可以是同步的也可以是异步的。同步处理程序在完成对为其调用该处理程序的 HTTP 请求的处理后才会返回。异步处理程序运行进程的行为与向用户发送响应无关。当您需要启动一个可能耗费很长时间的应用程序进程,而用户又无需等候进程完成以便从服务器获取响应时,异步处理程序非常有用。

ASP.NET 中的内置 HTTP 处理程序

ASP.NET 根据文件扩展名将 HTTP 请求映射到 HTTP 处理程序。每个 HTTP 处理程序都能够处理应用程序中的单个 HTTP URL 或 URL 扩展名组。ASP.NET 包括几种内置的 HTTP 处理程序,如下表所列。

1.jpg

2.jpg


如上面配置所示,.NET Framework配置中添加的Handler,实际后面还跟了一堆,不过都是同一种HttpFrobiddenHandler

创建自定义HTTP处理程序

若要创建一个自定义HTTP处理程序,可以创建一个可实现IHttpHandler 接口的类以创建同步处理程序,或者创建一个可实现IHttpAsyncHandler的类以创建异步处理程序。两种处理程序接口都要求您实现IsReusable属性和ProcessRequest方法IsReusable属性指定IHttpHandlerFactory对象(实际调用适当处理程序的对象)是否可以将您的处理程序放置在池中,并且重新使用它们以提高性能,或是否在每次需要处理程序时都必须创建新实例。ProcessRequest方法负责实际处理单个 HTTP 请求。

创建文件扩展名

创建一个类文件作为您的 HTTP 处理程序时,可以让您的处理程序响应尚未在 IIS 和 ASP.NET 中映射的任何文件扩展名。例如,如果您在创建用于生成 RSS 源的 HTTP 处理程序,则可以将处理程序映射到扩展名 .rss。为了让 ASP.NET 知道哪个处理程序将用于您的自定义文件扩展名,必须在 IIS 中将处理程序类文件的扩展名映射到 ASP.NET,并且在您的应用程序中将该扩展名映射到您的自定义处理程序。

默认情况下,ASP.NET 为自定义 HTTP 处理程序映射文件扩展名 .ashx 的方式与将扩展名 .aspx 映射到 ASP.NET 页处理程序的方式相同。因此,如果您创建具有文件扩展名 .ashx 的 HTTP 处理程序类,该处理程序将自动注册到 IIS 和 ASP.NET。

如果想要为您的处理程序创建自定义文件扩展名,则必须显式将该扩展名注册到 IIS 和 ASP.NET。不使用文件扩展名 .ashx 的好处是您的处理程序随后可以重新用于其他扩展名映射。例如,在某个应用程序中,您的自定义处理程序可能响应以 .rss 结尾的请求,而在另一个应用程序中,您的自定义处理程序可能响应以 .feed 结尾的请求。再举一例,您的处理程序可能映射到同一应用程序中的两个文件扩展名,但可能基于扩展名创建两个不同的响应。

下面通过一个例子来演示httpHandler的建立,注册以及效果

在App_Code中添加类ApkHandler实现接口IHttpHandler

namespace FastDoge.Study
{
    public class ApkHandler : IHttpHandler
    {
        public void ProcessRequest(HttpContext context)
        {
            HttpRequest Request = context.Request;
            HttpResponse Response = context.Response;
            // This handler is called whenever a file ending 
            // in .sample is requested. A file with that extension
            // does not need to exist.
            Response.Write("<html>");
            Response.Write("<body>");
            Response.Write("<h1>Hello from a synchronous custom HTTP handler.</h1>");
            Response.Write("</body>");
            Response.Write("</html>");
        }
        public bool IsReusable
        {
            // To enable pooling, return true here.
            // This keeps the handler in memory.
            get { return false; }
        }
    }
}

接着到Web.config中注册Hanler,这里对于两个不同版本的IIS也会有出入

在 IIS 6.0 和 IIS 7.0 经典模式下运行的Web.config添加以下配置

    <httpHandlers>
      <add verb="*" path="*.apk" 
        type="FastDoge.Study.ApkHandler" />
    </httpHandlers>

verb指定谓词列表可以是逗号分隔的 HTTP 谓词列表(例如,"GET, PUT, POST"),也可以是开始脚本映射(如星号 [*] 通配符)。

path:指定路径属性可以包含单个 URL 路径或简单的通配符字符串(如 *.aspx)。

type:指定逗号分隔的类/程序集组合。ASP.NET 首先在应用程序的专用 \bin 目录中搜索程序集 DLL,然后在系统程序集缓存中搜索程序集 DLL。

IIS7集成模式配置如下

  <system.webServer>
    <handlers>
      <add name="ApkHandler" verb="*"
        path="*.apk"
        type="FastDoge.Study.ApkHandler"
        resourceType="Unspecified" />
    </handlers>
  </system.webServer>

在运行后在浏览器中输入一个以apk为后缀的url

3.jpg

在MSDN中提到的可以在IIS中通过图形界面注册,这里就不尝试了,可参考https://msdn.microsoft.com/zh-cn/library/bb515343(v=vs.100).aspx。当然如果不在配置文件中添加,要是在HttpModule中指定,要与HttpModule耦合在一起的话就如上篇所说调用HttpContext.RemapHandler方法,如在上篇提到的MyModule类中作以下改动

        private void Application_BeginRequest(Object source,
        EventArgs e)
        {
            HttpApplication application = (HttpApplication)source;
            HttpContext context = application.Context;
            context.Response.Write("<h1><font color=red>" +
                "HelloWorldModule: Beginning of Request" +
                "</font></h1><hr>");
            string filePath = context.Request.FilePath;
            string fileExtension =
                VirtualPathUtility.GetExtension(filePath);
            if (fileExtension.Equals(".apk", StringComparison.InvariantCultureIgnoreCase) &&
                 context.Request.HttpMethod.Equals("GET"))
            {
                context.RemapHandler(new ApkHandler());
            }
        }

去掉Web.config的配置,访问以上URL有同样的效果。  

异步 HTTP 处理程序

利用异步 HTTP 处理程序可以启动一个外部进程(例如对远程服务器的方法调用),然后继续处理程序的处理工作,而无需等待外部进程结束。在异步 HTTP 处理程序的处理期间,ASP.NET 将通常用于外部进程的线程放回线程池中,直到处理程序收到来自外部进程的回调。这样可以避免阻止线程,并大幅改善了性能,因为一次所能执行的线程数量是有限的。如果许多用户都在请求依赖于外部进程的同步 HTTP 处理程序,那么操作系统可能很快就会用完所有线程,因为大量线程被阻止,正在等待外部进程。

创建异步处理程序时,除了实现IHttpAsyncHandler接口,还必须实现BeginProcessRequest以启动异步调用来处理单个HTTP请求。还必须实现EndProcessRequest方法,以便在进程结束时运行清理代码。

下面则定义了一个AsyncApkHandler的异步处理程序,在BeginProcessRequest时调用一个AsynchOperation,该类实现IAsyncResult接口,需要异步操作的代码在方法StartAsyncWork()中调用

namespace FastDoge.Study
{
    public class AsyncApkHandler : IHttpAsyncHandler
    {
        public bool IsReusable { get { return false; } }
  
        public IAsyncResult BeginProcessRequest(HttpContext context, AsyncCallback cb, Object extraData)
        {
            context.Response.Write("<p>Begin IsThreadPoolThread is " + Thread.CurrentThread.IsThreadPoolThread + " " + DateTime.Now + "  " + Thread.CurrentThread.ManagedThreadId + "</p>\r\n");
            AsynchOperation asynch = new AsynchOperation(cb, context, extraData);
            asynch.StartAsyncWork();
            return asynch;
        }
        public void EndProcessRequest(IAsyncResult result)
        {
            if (result is AsynchOperation)
            {
                (result as AsynchOperation).Context.Response.Write("<p>End IsThreadPoolThread is " + Thread.CurrentThread.IsThreadPoolThread + "  "+DateTime.Now+"  " + Thread.CurrentThread.ManagedThreadId + "</p>\r\n");
            }
        }
        public void ProcessRequest(HttpContext context)
        {
            throw new InvalidOperationException();
        }
    }
    class AsynchOperation : IAsyncResult
    {
        private bool _completed;
        private Object _state;
        private AsyncCallback _callback;
        private HttpContext _context;
        bool IAsyncResult.IsCompleted { get { return _completed; } }
        WaitHandle IAsyncResult.AsyncWaitHandle { get { return null; } }
        Object IAsyncResult.AsyncState { get { return _state; } }
        bool IAsyncResult.CompletedSynchronously { get { return false; } }
        public HttpContext Context
        {
            get
            {
                return _context;
            }
        }
        public AsynchOperation(AsyncCallback callback, HttpContext context, Object state)
        {
            _callback = callback;
            _context = context;
            _state = state;
            _completed = false;
        }
        public void StartAsyncWork()
        {
            ThreadPool.QueueUserWorkItem(new WaitCallback(StartAsyncTask), null);
        }
        private void StartAsyncTask(Object workItemState)
        {
            Thread.Sleep(3000);
            _context.Response.Write("<p>Completion IsThreadPoolThread is " + Thread.CurrentThread.IsThreadPoolThread + "  " + DateTime.Now + " " + Thread.CurrentThread.ManagedThreadId + "</p>\r\n");
            _context.Response.Write("Hello World from Async Handler!");
            _completed = true;
            _callback(this);
        }
    }
}

配置方式如之前的方式。效果如下

4.jpg

自定义IHttpHandlerFactory类

IHttpHandlerFactory类接收请求并负责向相应的HTTP处理程序转发请求。您可以通过创建一个实现了IHttpHandlerFactory接口的类来创建自定义 HTTP 处理程序工厂。创建自定义处理程序工厂可以更好地控制对HTTP请求的处理,因为这样可以基于运行时条件创建不同的处理程序。例如,使用自定义 HTTP 处理程序工厂,可以在 HTTP 请求方法为 PUT 时为某个文件类型实例化一个 HTTP 处理程序,而在该方法为 GET 时实例化另一个 HTTP 处理程序。又例如,通过使用 HTTP 处理程序工厂,可以创建有限数量的 HTTP 处理程序对象,来访问诸如数据库连接等昂贵或有限的资源。然后,可以在以后的请求中重用这些处理程序对象。

IHttpHandlerFactory有两个方法

IHttpHandler GetHandler:返回实现 System.Web.IHttpHandler 接口的类的实例;

void ReleaseHandler:使工厂可以重用现有的处理程序实例。

下面则定义个ApkHanlderFactory,同时在ApkHandler 输出的内容上有稍作修改(输出当前HttpHandler的HashCode)

namespace FastDoge.Study
{
    public class ApkHanlderFactory : IHttpHandlerFactory
    {
        private ApkHandler _cacheHandler;
        private ApkHandler CacheHandler
        {
            get
            {
                if (_cacheHandler == null)
                {
                    _cacheHandler = new ApkHandler();
                }
                return _cacheHandler;
            }
        }
        public IHttpHandler GetHandler(HttpContext context, string requestType, string url, string pathTranslated)
        {
            if (context.Request.QueryString.AllKeys.Contains("IsCache") &&
                context.Request["IsCache"].ToLower().Equals("true", StringComparison.InvariantCultureIgnoreCase))
            {
                return CacheHandler;
            }
            return new ApkHandler();
        }
        public void ReleaseHandler(IHttpHandler handler)
        {
            
        }
    }
}

配置文件方面与注册IHttpHandler基本一致,只是type特性中填写的是实现IHttpHandlerFactory接口的类名,但是在Module中通过编码的形式指定的方式暂时没找到,估计需要看源码了。

请求URL如下URL时,响应的html内容一直不变

5.jpg

如果去除IsCache参数时,内容则每次都在变化。

参考内容

HTTP 处理程序介绍

来自 <https://msdn.microsoft.com/zh-cn/library/ms227675(v=vs.100).aspx>

HTTP 处理程序和 HTTP 模块概述

来自 <https://msdn.microsoft.com/zh-cn/library/bb398986(v=vs.100).aspx>

想阅读更多技术文章,请访问听云技术博客,访问听云官方网站感受更多应用性能优化魔力。


关于作者

coco秋洁

我爱学习,学习使我快乐

我要评论

评论请先登录,或注册