Asp.net 处理程序(第五篇)

  HttpApplication有19个标准事件,当到达第8个事件PostMapRequestHandler触发的时候,标志着已经获取到了处理请求的处理程序对象,在第11个事件PreRequestHandlerExecute之后,HttpApplication将执行这个处理程序。

问题:

  1. HttpApplication如何选择处理程序?
  2. 处理程序是什么对象?
  3. HttpApplication如何得到这个处理程序对象?

一、处理程序

  针对不同的请求,Asp.net要有不同的处理,在Asp.net中通过各种处理程序来分别进行处理。通常情况下,根据请求的扩展名来确定处理程序。其实在Asp.net中已经预定义了很多处理程序。

  1、处理程序与HttpApplication的关系

  在Asp.net中,请求的真正处理是在处理程序这个环节。处理程序负责完成实际的请求处理工作,对于开发者来说,大多数的开发工作都是围绕着处理程序展开的。实际上,接触到HttpApplication事件处理的时候不多,HttpApplication除处理程序之外的作用,都可以理解为是为处理程序进行处理前的预处理和处理后的工作。

  处理程序在不同的网站开发技术中,有着不同的名称,在Asp.net中,称为HttpHandler。

  2、处理程序接口IHttpHandler和IHttpAsyncHandler

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

  IHttpHandler的定义如下:

    public interface IHttpHandler
    {
        bool IsReusable { get; }
        void ProcessRequest(HttpContext context);
    }

  一般处理程序里实现的,就是这个东西。

  • ProcessRequest是IHttpHandler接口的主要方法,接收并通过一个HttpContext类型的请求上下文对象,处理程序可以得到关于处理请求所需的信息。通过  HttpContext的Response属性可以得到响应的对象,用以向客户端返回服务器处理的结果。
  • IsReusable属性表示:“当这个处理程序对象在使用之后,是否还可以被缓存起来,在以后的请求处理中再次使用”,这个属性主要用来配合处理程序工厂使用。

  异步的处理程序派生自同步的处理程序接口,接口定义如下:

    public interface IHttpAsyncHandler : IHttpHandler
    {
        IAsyncResult BeginProcessRequest(HttpContext context, AsyncCallback cb, object extraData);
        void EndProcessRequest(IAsyncResult result);
    }

  比IHttpHandler增加了两个方法,BeginProcessRequest和EndProcessRequest方法。跟其他异步方法使用一样。

  3、在处理程序中使用会话

  处理程序是Asp.net网站中处理请求的基本单位。在默认情况下,处理程序中甚至不能使用会话状态,这样可以提高网站处理的速度。对于需要读写会话状态的处理程序,必须实现一个特定的标记接口IRequireSessionState,这个接口定义在命名空间System.Web.SessionState中,其中没有定义任何成员,所以,实现这个接口并不需要在类中增加任何成员。类似地,同样定义在这个命名空间下的接口IReadOnlySessionState也没有定义任何程序,用来标志只需要读取会话状态的处理程序。

  这种没有任何成员的接口,通常被称为标记接口,它的出现是由于.Net早期没有标签(Attribute)而昙花一现,在Asp.net中,这是仅有的一例。

  HttpApplication的第9个事件AcquireRequestState事件和第13个事件ReleaseRequestState中,HttpApplication即通过判断已经获取得处理程序对象是否实现了这些接口来决定请求状态的处理工作。

  4、处理程序工厂

  实现了处理程序接口的类就可以被用来创建处理程序对象直接使用,如果再配合一个处理程序工厂,那么就可以实现处理程序对象的管理。

  比如,创建一个处理程序对象池,就可以不用在每次使用处理程序的时候创建一个新的对象,而是可以从池中取出一个现有的对象直接使用,以提高效率。

  在Asp.net中,作为处理程序工厂的类必须实现接口IHttpHandlerFactory,这个接口在命名空间System.Web下,定义为:

    public interface IHttpHandlerFactory
    {
        IHttpHandler GetHandler(HttpContext context, string requestType, string url, string pathTranslated);
       void ReleaseHandler(IHttpHandler handler);
    }

  其中GetHandler方法用来通过这个处理程序工厂获取一个处理程序对象,ReleaseHandler方法用来释放一个处理程序对象。

  关系图如下:

  

  5、注册处理程序

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

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

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

  如果我们定义了一个处理程序类,这个类定义在命名空间MyHandler下,名为ValidateCodeHandler,这个类定义在私有程序集MyHandler.dll中,用来处理GET类型的请求,请求的扩展名为vc,那么配置参数如下:

<configuration>
  <system.web>
    <httpHandlers>
      <add verb="GET" path="*.vc" type="MyHandler.ValidateCodeHandler,MyHandler.dll"/>
    </httpHandlers>
  </system.web>
</configuration>

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

  6、使用处理程序生成验证码

  验证码是网站开发中常使用的安全技术,用于防止恶意的攻击。

  我们通过一个处理程序来生成发送到客户端的验证码图片,完成这个处理程序需要以下步骤:

  1. 创建一个类库项目MyHandler,新的项目的默认命名空间也是MyHandler。
  2. 在类库项目MyHandler中添加一个新的类ValidateCodeHandler,实现IHttpHandler接口。为了在处理程序中使用Session状态管理,同时实现IRequiresSessionState接口。
  3. 在ProcessRequest方法中完成验证码生成的操作。
  4. 在网站项目中引用这个类库项目。
  5. 在网站项目的配置文件web.config中注册这个处理程序。
  6. 在需要验证码的页面上使用处理程序生成的验证码。

  新建一个Asp.net Web程序:

    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; }
        }

  然后在web.config里面增加如下配置:

    <httpHandlers>
      <add verb="GET" path="*.vc" type="WebApplication1.ValidateCodeHandler"/>
    </httpHandlers>

  然后启动项目,随便打开一个本系统下.vc后缀的路径:

  

  从项目中我们可以看到,所有.vc后缀的路径,Asp.net都会映射到这个类中处理。

二、一般处理程序

  虽然通过标准的方式可以创建处理程序,但是实现的步骤比较复杂,为了方便网站开发中对于处理程序的应用,从Asp.net 2.0开始,Asp.net提供了称为一般处理程序的处理程序,允许我们私用比较简单的方式定义扩展名为ashx的专用处理程序。

  通过Visual Studio 2010创建的一般处理程序将会生成两个文件Handler1.ashx和Handler1.ashx.cs。其中Handler1.ashx文件中的内容如下所示:

<%@ WebHandler Language="C#" Class="Handler" %>

  对应的代码文件Handler1.ashx.cs中的内容如以下代码所示。可以非常清楚地看到,这就是一个实现IHttpHandler接口的类,如下:

    public class Handler1 : IHttpHandler
    {

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

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

  一般处理程序实际上就是一个处理程序类,这个处理程序被Asp.net直接在系统的配置文件中映射到了ashx扩展名的请求上。这样,我们就不需要在配置文件中进行配置了。

  1、一般处理程序工厂

  对于一般处理程序来说,扩展名请求的程序并不需要程序员在web.config中进行配置,这是因为这个处理程序已经定义在系统配置文件的web.config。所以浏览器可以直接请求扩展名为ashx的地址:

<add path="*.ashx" verb="*" type="System.Web.UI.SimpleHandlerFactory" validate="True" />

  通过配置文件,我们可以看到,对于扩展名为ashx的请求是通过定义在命名空间System.Web.UI下的SimpleHandlerFactory处理程序工厂来完成的。

  当请求一个ashx扩展名的服务器上的资源的时候,SimpleHandlerFactory将找到对应的ashx文件,通过这个文件找到对应的处理程序,最后,SimpleHandlerFactory通过反射创建一个此类型的处理程序对象实例,并通过GetHandler方法返回给HttpApplication,完成最终的请求处理过程。

  2、使用一般处理程序的场合

  对于Asp.net网站来说,最常见的处理结果就是HTML网页,生成网页的工作通常使用扩展名为aspx的Web窗体来完成,对于处理结果不是HTML的请求,都可以通过一般处理程序来完成。例如生成RSS Fed,XML,图片等。

  一般处理程序是Asp.net网站中最为简单、高效的处理程序,在处理返回类型不是HTML的请求中有着重要的作用。

  3、使用一般处理程序生成验证码图片

  例如,在前面实现的验证码处理程序用一般处理程序能够更快地完成,直接在ProcessRequest方法中实现功能就可以了,原来的第1、2、4、5步都可以省略。

    public class Handler1 : 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;
            }
        }
    }

  一般处理程序要使用Session也是要实现接口IRequiresSessionState的。

  4、使用一般处理程序生成JSON

   代码示例:

    public class UploadPercentHandler : IHttpHandler
    {

        public void ProcessRequest(HttpContext context)
        {
            Person p = new Person();
            p.Id = 1;
            p.Name = "撼地神牛";

            JavaScriptSerializer jss = new JavaScriptSerializer();
            string json = jss.Serialize(p); 
            context.Response.ContentType = "application/json";
            context.Response.Write(json);
        }

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

    public class Person
    {
        public int Id { get; set; }
        public string Name { get; set; }
    }

  输出:

  

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。