一、概述:
其实每个.aspx页面都是一个HttpHandler,因为System.Web.UI.Page类实现了IHttpHandler接口。一般情况下,响应客户端请求的都是.aspx页面,这时候System.Web.UI.Page类可以作出合适的处理。但有时候响应给客户端的不一定就是.aspx页面,有可能是XML或者图像等,这个时候就需要自定义HttpHandler进行处理。
常用的HttpHandler处理有构造缩略图、图片加水印。一般在网站中,比如一个购物网站的商品列表里,显示的商品图片都是经过缩略处理。大家可以想象到,如果直接把原图返回到客户端显示,网络流量是如此巨大,这样会严重影响页面显示速度。同样,很多网站上的图像资源都是原创的,为了保证版权,防止不法盗用,都会在图片返回到客户端之前对图片进行处理,加上数字水印标记。为图片资源加上水印,可以防止原创图片被非法盗用,而且使用编程方式添加可以在不破坏原图的情况下对图片进行处理。
HttpHandler与HttpModule区别:
两者的区别:
a. 先后次序:
数据先经过HttpModule,再经过HttpHandler,然后又返回到HttpModule。注意,Module要判断响应的是哪个事件,有些事件是在Handler之前执行,有些是在Handler之后执行。
b.对请求的处理:
HttpModule无论客户端请求的是什么文件,它都会对请求进行拦截处理,例如.aspx,.rar,.html,.jpg等等的请求。HttpHandler只有在ASP.NET注册过的文件类型(例如aspx,asmx等等)才会调用它。
c.HttpHandler是请求的最终处理中心,按照请求生成响应的内容。HttpModule对请求进行预处理,如验证、修改、过滤等等,同时也可以对响应进行处理。
二、IHttpHandler
IHttpHandler接口:定义ASP.NET为使用自定义Http处理程序同步处理Http Web请求而实现的协定。Http处理程序是实现了System.Web.IHttpHandler接口的.NET组件,接口是一种规范,因此任何实现了IHttpHandler接口的类都可以用于处理输入的Http请求。
Http处理程序可分为同步处理程序、异步处理程序。同步处理程序必须继承IHttpHandler接口,异步处理程序必须继承IHttpAsyncHandler接口。
public interface IHttpHandler { // 摘要: // 获取一个值,该值指示其他请求是否可以使用System.Web.IHttpHandler实例。 // 返回结果: // 如果System.Web.IHttpHandler实例可再次使用,则为true;否则为false。 bool IsReusable { get; } // 摘要: // 通过实现System.Web.IHttpHandler接口的自定义HttpHandler启用HTTP Web请求的处理。 // 参数: // context: // System.Web.HttpContext 对象,它提供对用于为 HTTP 请求提供服务的内部服务器对象(如 Request、Response、Session、和Server)的引用。 void ProcessRequest(HttpContext context); }
1.0 IsReusable //IsReusable属性指定IHttpHandlerFactory对象(实际调用适当处理程序的对象)是否可以将处理程序放置在由CLR会维护的一个对象池中,并且重新使用它们以提高性能,或是否在每次需要处理程序时都必须创建新实例。如果IHttpHandler实例可再次使用,则为true,否则为false。 //Page类上的IsReusable属性返回false,表示需要该Http请求的新实例来服务一个页面请求。通常我们使它在所有情况下都返回false,并根据请求负荷的不同而要求它做一些有意义的处理。那些不依赖于context.Request对象,也没有成员变量,被用作筛选特殊请求的简单屏障的处理程序可以将IsReusable设置为true,以节省一些系统资源。 2.0 ProcessRequest //顾名思义,ProcessRequest就是请求响应,是Http请求的最终处理方法。一个Http请求都是最终交给一个HttpHandler容器中的ProcessRequest方法来处理。方法中应该放置我们处理请求的主要代码。
在ProcessRequest方法中,需要一个HttpContext类型的参数,这个参数也称为上下文。它封装有关个别 Http请求的所有Http特定的信息。在HttpContext对象中可以获得用于为Http请求提供服务的内部服务器对象(如Request、Response和Server)的引用。当客户端发送某个Http请求,我们可以通过HttpContext进行截获,查看里面所包含的请求信息,可以进行一系列的操作。
下面以图片添加数字水印例子简述ProcessRequest方法的用法:
首先创建HttpHandler处理类。在项目中添加一个实现了IHttpHandler接口的类,这里我们把HttpHandler处理类命名为“CoverHandler”。代码如下所示:
using System; using System.Web; public class CoverHandler: IHttpHandler { //需要继承IHttpHandler接口 public CoverHandler() { } //水印图片路径 private const string WATERMARK_URL = "~/Image/watermark.bmp"; //默认图片路径 private const string DEFAULTIMAGE_URL = "~/Image/default.bmp"; public void ProcessRequest (HttpContext context) { System.Drawing.Image Cover; //判断请求的物理路径中是否存在被请求的图片文件 if (File.Exists(context.Request.PhysicalPath)) { //加载图片文件 Cover = Image.FromFile(context.Request.PhysicalPath); //加载水印图片 Image watermark = Image.FromFile(context.Request.MapPath(WATERMARK_URL)); //实例化画布 Graphics g = Graphics.FromImage(Cover); //在image上绘制水印 //Rectangle部分设置画的大小,图片根据它来缩放。后面的指定要用来画上去的图片,画那一部分。(起始点大小,代表载取的图片放到前面的指定大小里面去) 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); //释放画布 g.Dispose(); //释放水印图片 watermark.Dispose(); } else { //如果被请求的图片不存在时,加载默认图片 Cover = Image.FromFile(context.Request.MapPath(DEFAULTIMAGE_URL)); } //设置输出格式 cotext.Response.ContentType = "image/jpeg"; //将图片存入输出流 Cover.Save(context.Response.OutputStream, System.Drawing.Imaging.ImageFormat.Jpeg); Cover.Dispose(); context.Response.End(); } public bool IsReusable { get { return false; } } }
三、全局配置HttpHandler
要让系统自动捕获对图片访问的请求,需要在项目里Web.config配置文件中添加httpHandlers配置节进行配置。代码如下所示:
<system.web> ...... <httpHandlers> <add path="*.jpg" verb="*" validate="false" type="CoverHandler"/> </httpHandlers> </system.web> 各项属性代表含义如下: path:必选属性。指定路径属性可以包含单个URL路径或简单的通配符字符串(如 *.aspx)。“*”为通配符。 verb:必选的属性。指定谓词列表可以是逗号分隔的Http谓词列表(例如,“GET, PUT, POST”),也可以是开始脚本映射。“*”为通配符。 validate:可选属性。如果为false,则ASP.NET在实际匹配请求到达之前将不尝试加载该类。这有可能延迟错误,但减少了启动时间。 type:必选属性。指定逗号分隔的类/程序集组合。ASP.NET首先在应用程序的专用in目录中搜索程序集DLL,然后在系统程序集缓存中搜索程序集DLL。
这样,当客户端发送访问jpg文件时,系统会自动截获请求转交给CoverHandler类处理。
但要注意的是,使用这种配置方式,在开发服务器上运行时没有问题。但如果在IIS上运行(经典模式),将会没有任何效果。这是因为开发服务器仅提供最简单的Web服务器功能,它不对请求的内容做任何处理,而是直接将所有的请求转交给ASP.NET处理。IIS是一个比较完善且功能强大的Web服务器。所有提交到IIS的请求,会在IIS上做一些分类处理,所依据的原则就是后缀名。默认情况下,.html、.jpg等静态格式的文件IIS会自己处理,直接将结果返回到客户端。只有当后缀名符合相关条件的(如.aspx),才将请求转交给ASP.NET进行处理。
也就是说IIS根本没有把请求提交给ASP.NET处理。因此我们需要对IIS作配置。
打开IIS信息服务管理器,在IIS栏目下打开“处理程序映射”功能。
可以看到不同后缀名的请求对应不同的处理程序。点击界面右侧“添加脚本映射”。
填写相关内容后点击“确定”,新的映射添加成功,并出现在映射列表中
这样当客户端请求的内容带有.jpg后缀名时,IIS即会把该请求转交给ASP.NET处理。达到返回的是带水印的图片。
四、局部调用HttpHandler
如果不需要对全部图片资源都添加数字水印,可以不采用上一节中说到的全局配置HttpHandler处理类的方式,而是使用一般处理程序对图片资源进行处理。
选中项目点击右键,选择“添加新项”,选择“一般处理程序”,这里我们把一般处理程序命名为“PictureWaterMark”。系统创建的PictureWaterMark.ashx一般处理程序代码;代码跟我们上面的基本一致,多了一个接受图片路径参数的,
string covername = context.Request.QueryString["covername"];调用的时候,可以:
<asp:Image ID="Image1" runat="server" ImageUrl='<%# Eval("covername", "~/ PictureWaterMark.ashx?covername={0}") %>' />
全局配置与局部调用HttpHandler两种方式各有优缺点,全局的配置是全面覆盖的,无论在哪个页面访问图片都被加上水印,而局部调用一般处理程序的方式只会在指定的访问时才加水印,它的使用比较灵活,但不便于维护。在开发过程中可以根据实际需求情况考虑使用那一种方式。
五、HttpHandler访问Session
在HttpHandler处理程序中,虽然ProcessRequest中有HttpContext对象,但在这里context对象却不能访问Session对象,使开发变得非常不方便。为了使HttpHandler处理程序能够访问Session对象,在类中除了继承IHttpHandler接口外,还需要引用System.Web.SessionState命名空间,并实现接口IReadOnlySessionState或IRequiresSessionState。
如果要在HttpHandler中读取Session的内容,就要实现IReadOnlySessionState 这个接口。
如果要在HttpHandler中读写Session的内容,就要实现IRequiresSessionState这个接口。
这两个接口没有需要实现的方法,可直接使用。两个接口的声明如下所示:
IRequiresSessionState接口: namespace System.Web.SessionState { // 摘要: // 指定目标 HTTP 处理程序需要对会话状态值具有读写访问权。这是一个标记接口,没有任何方法。 public interface IRequiresSessionState { } } IReadOnlySessionState接口: namespace System.Web.SessionState { // 摘要: // 指定目标 HTTP 处理程序只需要具有对会话状态值的读访问权限。这是一个标记接口,没有任何方法。 public interface IReadOnlySessionState : IRequiresSessionState { } } 这里能看到,其实IReadOnlySessionState接口是继承了IRequiresSessionState接口。