大家在用HttpHandler的时候,一般都会有两个大的疑问(当然,前提是你有钻研精神的话,呵呵)

1. IsReusable到底什么意思?

老实说,这个属性很多人都感兴趣,但搞懂的人确实不多。MSDN中的介绍也是不知所以然。

获取一个值,该值指示其他请求是否可以使用 IHttpHandler 实例。该属性默认为false

我来这么说吧,首先我们为什么使用自定义的Handler呢?简单的说,我们是希望能接管掉某些请求, 对吧?最常见的应用如下

对图片进行处理。例如所有图片都输出一个水印。或者防止盗链到设计

添加一些特殊的扩展名。例如,我的网站能不能有一个后缀名为chenxizhang的网页呢?(这当然是一 个比喻,事实上一般没有必要这么做)

知道上述的需求之后,我们再来看一下后台的设计。HttpHandler其实就是实现了IHttpHandler接口的 一个类型,它要工作,就必须通过 ASP.NET所提供的一些所谓的Factory去创建实例,然后调用它的 ProcessRequest方法。其实就这么简单

因为创建对象实例在服务器肯定是需要占用资源的,那么我们就势必要考虑这些请求能不能在一定程 序上去复用。这就是IsReusable的初衷

事实上,我们对这种复用并不会陌生。平常我们就知道对象池和连接池的技术。Handler的Reuse也是 一个池的概念。

好了,说了这么一堆的概念,我们来讲一讲该属性设置为true和设置为false的区别。

设置为true,则通常情况下,就创建一次实例

设置为false,则每次请求都需要创建实例

我们来看一个例子。为了做测试,我写了如下代码

using System;
using System.Collections.Generic;
using System.Web;
using System.IO;
using System.Threading; 
namespace WebApplication1
{
    public class TestHandler:IHttpHandler
    { 
        #region IHttpHandler 成员 
        public bool IsReusable
        {
            get { return fasle; }
        } 
        public void ProcessRequest(HttpContext context)
        {
            WriteLogMessage("当前被调用的请求是:" + context.Request.Path);
            context.Response.Write("你请求的地址是:" +  context.Request.Url.ToString() + "");
            WriteLogMessage("结束调用");
        } 
        #endregion 
        public TestHandler() {
            WriteLogMessage("对象实例被创建");
        } 
        void WriteLogMessage(string msg)
        {
            string logFile = HttpContext.Current.Server.MapPath("Log.txt");
            File.AppendAllText(logFile, DateTime.Now.ToString()+ msg +  Environment.NewLine);
        }
    }
}

代码很简单,我通过一个文本文件的方式记录对象什么创建的,什么时候发起请求和结束请求的。现 在我先设置IsReusable为false

在调试之前,我们还需要设置一下web.config,添加一个HttpHandler的注册

    

这里作为演示目的,我就牺牲一下自己,用这个Handler来监听所有后缀名为chenxizhang的请求

打开浏览器,输入类似下面这样的网址http://localhost:6994/test.chenxizhang

快速地刷新几次,然后关闭浏览器。找到网站根目录下面的一个Log.txt文件,会看到下面的文本

2009/6/15 11:28:30对象实例被创建
2009/6/15 11:28:30当前被调用的请求是:/test.chenxizhang
2009/6/15 11:28:30结束调用
2009/6/15 11:28:31对象实例被创建
2009/6/15 11:28:31当前被调用的请求是:/test.chenxizhang
2009/6/15 11:28:31结束调用
2009/6/15 11:28:31对象实例被创建
2009/6/15 11:28:31当前被调用的请求是:/test.chenxizhang
2009/6/15 11:28:31结束调用
2009/6/15 11:28:32对象实例被创建
2009/6/15 11:28:32当前被调用的请求是:/test.chenxizhang
2009/6/15 11:28:32结束调用
2009/6/15 11:28:32对象实例被创建
2009/6/15 11:28:32当前被调用的请求是:/test.chenxizhang
2009/6/15 11:28:32结束调用
2009/6/15 11:28:32对象实例被创建
2009/6/15 11:28:32当前被调用的请求是:/test.chenxizhang
2009/6/15 11:28:32结束调用
2009/6/15 11:28:32对象实例被创建
2009/6/15 11:28:32当前被调用的请求是:/test.chenxizhang

我们可以看到,每次都请求都需要创建实例

然后,我们去修改一下IsReusable属性为true,再运行,就可以看到下面这样的输出结果了

2009/6/15 11:23:34对象实例被创建
2009/6/15 11:23:34当前被调用的请求是:/test.chenxizhang
2009/6/15 11:23:34结束调用
2009/6/15 11:24:40当前被调用的请求是:/test.chenxizhang
2009/6/15 11:24:40结束调用
2009/6/15 11:24:40当前被调用的请求是:/test.chenxizhang
2009/6/15 11:24:40结束调用
2009/6/15 11:24:40当前被调用的请求是:/test.chenxizhang
2009/6/15 11:24:40结束调用
2009/6/15 11:24:40当前被调用的请求是:/test.chenxizhang
2009/6/15 11:24:40结束调用
2009/6/15 11:24:40当前被调用的请求是:/test.chenxizhang
2009/6/15 11:24:40结束调用
2009/6/15 11:24:41当前被调用的请求是:/test.chenxizhang
2009/6/15 11:24:41结束调用
2009/6/15 11:24:41当前被调用的请求是:/test.chenxizhang
2009/6/15 11:24:41结束调用

也就是说,对象只被创建了一次。后面调用就重复利用了。这样看起来是不是还不错呢?

通常意义上说,IsReusable设置为true可以提高性能,尤其是说这种Handler初始化的时候需要做很多 事情的情况下。但为什么默认又设置为false呢?

因为如果设置为true,也就是所有有用户,所有请求都共享一个对象实例,那么可能造成什么问题呢 ?

最典型的问题就是你要注意这个类型中成员变量的线程安全性问题,例如某个请求上来之后,修改了 某个变量;然后做其他的事情,紧接着其他请求也上来了,它又修改了变量,这可能就有问题。

这一点我倒认为没有什么大不了的,我也算是写过一些Handler的,但其实比较少真的复杂到要搞一堆 变量。如果是那样,是需要检讨的

我同时还想到另外一个问题,假设IsReusable是设置为true的,同时又假设它的ProcessRequest却又 需要比较长时间才能完成。那么会怎么样呢?

就是说前一个请求还没有完成,这时候又有第二个请求过来。该如何处理?

using System;
using System.Collections.Generic;
using System.Web;
using System.IO;
using System.Threading;
using System.Web.SessionState; 
namespace WebApplication1
{
    public class TestHandler:IHttpHandler
    { 
        #region IHttpHandler 成员 
        public bool IsReusable
        {
            get { return true; }
        } 
        public void ProcessRequest(HttpContext context)
        {
            WriteLogMessage("当前被调用的请求是:" + context.Request.Path);
            context.Response.Write("你请求的地址是:" +  context.Request.Url.ToString() + "");
            Thread.Sleep(10000);
            WriteLogMessage("结束调用");
        } 
        #endregion 
        public TestHandler() {
            WriteLogMessage("对象实例被创建");
        } 
        void WriteLogMessage(string msg)
        {
            string logFile = HttpContext.Current.Server.MapPath("Log.txt");
            File.AppendAllText(logFile, DateTime.Now.ToString()+ msg +  Environment.NewLine);
        }
    }
}

上面的代码中,我加入一个让当前线程休眠的代码:让它休眠10秒钟。此时,我们来刷新浏览器看看 会怎么样

2009/6/15 12:05:59对象实例被创建
2009/6/15 12:05:59当前被调用的请求是:/ie.chenxizhang
2009/6/15 12:06:02对象实例被创建
2009/6/15 12:06:02当前被调用的请求是:/google.chenxizhang
2009/6/15 12:06:09结束调用 //这一句是ie.chenxizhang结束了
2009/6/15 12:06:12结束调用 //这一句是google.chenxizhang结束了

我用了两个浏览器来模拟两个用户的操作。你会惊奇地发现,虽然我们设置为true,但仍然创建了多 个实例。这又是咋回事呢

这个问题要这么理解了:因为现在是线程阻塞了,所以ASP.NET引擎会检测到这一点,他必须创建另外 实例出来,否则很显然,第二个请求就必须等第一个请求结束之后才能开始工作,这显然是不可以接受的 ,这个现象,在大容量用户并发的时候可能是一个灾难

好吧,再绕回来说,假如我们的操作时间比较长,但什么时候又不需要创建多个实例呢?答案就是说 ,如果异步的话。

也正因为可能会有异步的情况,所以就出现了我们刚才所说的线程安全性问题。可以配合锁定机制避 免一些问题。

所以,针对IsReusable属性,我总结如下:

这个属性默认为false(Visual studio提供的模板默认将其设置为false)

如果设置为true,能提高性能,但要注意线程之间安全性问题

如果设置为false,则线程是安全的

2. 如何在Handler中使用会话状态(Session)?

有些朋友 在使用Handler的时候,念念不忘原先在页面编程中的会话状态(Session),一个典型的问题就是:在 Handler里面能不能使用我的用户状态信息呀?

例如,能不能添加下面这样的代码呢

context.Response.Write("当前用户的状态值为:" + context.Session ["Id"].ToString());

答案是:当然可以。

先别忙着乐,虽然可以添加这样的代码,但它默认却无法工作。你会收到下面这样的错误消息:未将 对象设置到引用的实例

其实,一个比较好的建议是:不要在Handler里面使用会话状态。甚至在整个网站都不要。

但是,假设你就是认准了这个非用不可,那么你也可以通过下面的方式来实现

为该类型实现一个接口:IRequiresSessionState

这个接口不需要有任何方法实现,只需要定义一下就可以了。所以整个案例的代码,如下

using System;
using System.Collections.Generic;
using System.Web;
using System.IO;
using System.Threading;
using System.Web.SessionState;
namespace WebApplication1
{
public class TestHandler:IHttpHandler,IRequiresSessionState
{
#region IHttpHandler 成员
public bool IsReusable
{
get { return true; }
}
public void ProcessRequest(HttpContext context)
{
WriteLogMessage("当前被调用的请求是:" + context.Request.Path);
context.Response.Write("你请求的地址是:" + context.Request.Url.ToString() + "");
context.Response.Write("当前用户的状态值为:" + context.Session ["Id"].ToString());
WriteLogMessage("结束调用");
}
#endregion
public TestHandler() {
WriteLogMessage("对象实例被创建");
}
void WriteLogMessage(string msg)
{
string logFile = HttpContext.Current.Server.MapPath("Log.txt");
File.AppendAllText(logFile, DateTime.Now.ToString()+ msg + Environment.NewLine);
}
}
}

以上两点是编写HttpHandler中的难点和困惑点,大家可以参考领会一下。

asp.net Handler中的IsReusable属性及在Handler中使用Session的更多相关文章

  1. 使用 JavaScript 中的 document 对象的属性,根据下拉框中选择的属性,更改页面中的字体颜色和背景颜色

    查看本章节 查看作业目录 需求说明: 使用 JavaScript 中的 document 对象的属性,根据下拉框中选择的属性,更改页面中的字体颜色和背景颜色 实现思路: 在页面的 <body&g ...

  2. vue中computed计算属性与methods对象中的this指针

    this 指针问题 methods与computed中的this指针 应该指向的是它们自己,可是为什么this指针却可以访问data对象中的成员呢? 因为new Vue对象实例化后data中的成员和c ...

  3. js中如何判断属性是对象实例中的属性还是原型中的属性

    ECMAScript5中的hasOwnProperty()方法,用于判断只在属性存在与对象实例中的时候,返回true,in操作符只要通过对象能访问到属性就返回true. 因此只要in操作符返回true ...

  4. 在CSS中通过@font-face属性来实现网页中嵌入特殊字体。

    首先获取要使用字体的三种文件格式.EOT..TTF或.OTF..SVG,确保能在主流浏览器中都能正常显示该字体..EOT,适用于Internet Explorer 4.0+.TTF或.OTF,适用于F ...

  5. DOM 中的 id 属性会往全局变量中添加 id 值的变量

    一直没注意到这个坑,今天看<你不知道的 JavaScript>中提到了,今后需要注意. <!DOCTYPE html> <html> <head> &l ...

  6. <c:out>标签中的escapeXML属性

    <c:out>标签中的escapeXML属性 在<c:out>中,escapeXML属性默认为true. 当设置escapeXML的属性为true时,将value中的值以字符串 ...

  7. struts2.Action中的method属性配置

    .Action中的method属性 在struts1.x中我们知道通过继承DispatchAction可以实现把多个Action进行统一操作,在struts2中实现action的统一操作也很简单.我们 ...

  8. js中对象的属性名和属性值

    代码 /** * 对象的属性名 * - 对象的属性名不强制遵循标识符的命名规范,可以是任意的名字,但在开发中 * 尽量遵循标识符的命名规范 */ // 创建对象obj1 var obj1 = new ...

  9. ASP.NET MVC之表单集合数据自动绑定到对象属性(集合)中

    前言 之前没遇到过这个问题,在项目中遇到这个问题时想法挺好,按照流程走下去,结果事与愿违,于是开始探索着解决方案,接下来我们来看看这个问题,早已经明了的童鞋请绕道,此文仅供未遇到的童鞋提供一种解决方案 ...

随机推荐

  1. 网络流(最大流)CodeForces 512C:Fox And Dinner

    Fox Ciel is participating in a party in Prime Kingdom. There are n foxes there (include Fox Ciel). T ...

  2. 使用sqlhelper的简单增删改查

    一:所说的简单的三层构架,就是说没有业务逻辑层,将各层没有放到单独的项目中,解决方案如下: 二:form1.cs的详细代码 using System; using System.Collections ...

  3. 使用LoadRunner对Web Services进行调用--Import Soap

    利用LoadRunner对Web Services进行测试时,通常有三种可供采用的方法: 在LoadRunner的Web Services虚拟用户协议中,[Add Service Call] 在Loa ...

  4. C/S结构与B/S结构的特点分析

    C/S结构与B/S结构的特点分析 为了区别于传统的C/S模式,才特意将其称为B/S模式.认识到这些结构的特征,对于系统的选型而言是很关键的. 1.系统的性能 在系统的性能方面,B/S占有优势的是其异地 ...

  5. Linux下的各种软件安装方法汇总

    1 RPM包 1.1 安装 RPM包就像Windows的EXE安装文件一样,各种文件都已经编译好了,并进行了打包,哪一个文件应该放在哪一个目录下都指定好了,安装非常方便,在图形界面里你只需要双击就能自 ...

  6. 关于Marsedit和我的163博客

    其实我一开始选择的并不是163的博客,感觉没什么新意,勾不起我的兴趣. 第一个我想写的是CSDN的博客,代码嘛,总要找个专业的网站来写,但是我发现了一个及其让我不爽的事情,我竟然在网页上无法新建一篇日 ...

  7. linux0.12 编译过程

    感谢这篇文章的作者:    http://www.cnblogs.com/strugglesometimes/p/4231359.html 编译是个很蛋疼的事情,本想把linux0.12在bochs上 ...

  8. 关于数据库一致改关闭下redo日志文件丢失的处理办法的总结

    数据库一致性关闭下redo日志文件丢失的处理办法(归档和非归档都行) 1. inactive log  在一致性关闭后删除重启时可以在mount下(不丢失数据) alter database clea ...

  9. sBPM产品介绍

    作者:CppExplore   http://www.cppblog.com/CppExplore/和 http://blog.csdn.net/cppexplore同步发布. 近3年没发文章,谨以本 ...

  10. AnonymousType匿名类型和对象之间的转换

    本文转载:http://www.cnblogs.com/dean-Wei/p/3150553.html 一.匿名对象转换为对象. 1.问题: 2.解决方案:强制指定类型. 解决之. 二. 对象转换为匿 ...