博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

ASP.NET的错误处理机制

Posted on 2008-06-13 14:21  庞滨  阅读(228)  评论(0编辑  收藏  举报
ASP.NET的错误处理机制

对于一个Web应用程序来说,出错是在所难免的,因此我们应该未雨绸缪,为可能出现的错误提供恰当的处理。事实上,良好的错误处理机制正是衡量Web应用程序好坏的一个重要标准。试想一下,当用户不小心在浏览器输入了错误的URL或者当用户提供了一些信息导致程序出错的时候,如果我们没有对这些情况进行处理,而是任由404或是500的错误页面甚至出错的堆栈信息呈现在用户面前,这无疑会把一些用户给吓跑。所以,在我们开发Web应用程序的时候,应该对错误处理机制有充分的了解。

   
让我们回到ASP.NET上来,先提两个问题让大家思考一下:ASP.NET为我们提供了几种错误处理机制呢?如果同时采用了几种错误处理机制,它们之间是否存在一定的优先级呢?带着这个问题,我们先来看一下我们最常见的Web.Config文件:

<?xml version="1.0"?>
<configuration>
<system.web>
<customErrors mode="On" defaultRedirect="GenericErrorPage.htm">
<error statusCode="403" redirect="Error403.htm" />
<error statusCode="404" redirect="Error404.htm" />
</customErrors>
</system.web>
</configuration>


对于<customErrors>这个设置项,我想无需多言了,详情可以参考MSDN的。第一种错误处理机制——使用Web.Config<customErrors>配置项应该是大家最常用的。
接着,我们再看另外一个也很常用的文件:Global.asax。提到这个文件,大家想到了什么呢?对,就是跟两大Web应用程序对象(ApplicationSession)相关的事件了。在这些事件当中,有一个属于Application范畴的与错误相关的事件——Error,而对应的事件处理方法就是Application_Error了。顾名思义,这个事件处理方法在应用程序级别错误发生的时候就会被调用,因此你可以在这个方法中添加代码来对错误进行处理,如下所示:

protected void Application_Error(object sender, EventArgs e) {
Exception objErr = Server.GetLastError().GetBaseException();
Response.Write("Error:" + objErr.Message);
Server.ClearError();
}

在这里,大家要注意最后一句代码Server.ClearError()的使用,为什么要使用这句代码呢?如果不用又会怎样呢?在这里我又先卖个关子。好了,第二种错误处理机制——使用Global.asax中的Application_Error事件处理方法也登台亮相了。

以上这两种错误处理方法都可以说是全局性的,一个源自应用程序配置文件,一个则是必须放在应用程序根目录下的Global.asax文件的事件处理方法。与全局相对的就是局部,所以我们很自然的就会想:有没有应用于局部——某个页面的错误处理机制呢?答案是有的,而且还有两种————使用ErrorPage属性以及使用Page_Error事件处理方法。对于第一种机制,你几乎可以在任何时候设置ErrorPage属性,从而确定页面发生错误的时候会重定向至哪个页面;对于第二种机制而言,它与Application_Error事件处理方法是很类似的,只不过被触发的时机不同而已。以下是具体的两个例子:

<script language="C#" runat="server">
protected void Page_Load(object sender, EventArgs e) {
this.ErrorPage = "ErrorPage.htm";

}
</script>


protected void Page_Error(object sender, EventArgs e) {
Exception objErr = Server.GetLastError().GetBaseException();
Response.Write("Error:" + objErr.Message);
Server.ClearError(); //
同样要注意这句代码的使用
}

至此,四种错误处理机制已经悉数登场,是时候给它们排个名次了。根据优先级从高到低排序:Page_Error事件处理方法 > ErrorPage属性 > Application_Error事件处理方法 > <customErrors>配置项。虽然排序是这样,但是这个排序之间又有微妙的关系。首先,要让ErrorPage属性能够发挥作用,<customErrors>配置项中的mode属性必须设为"On";其次,虽然Page_Error事件处理方法排在最前面,但是,如果少掉了Server.ClearError()方法的话,仍然会引发优先级较低的错误处理,也就是说ErrorPage属性等错误处理机制仍然会发挥作用,这样就得不到你想要的结果了。这种情况对于Application_Error事件处理方法也是如此。顺序是排好了,但是顺序却不是最重要的问题,甚至可以说是没有太多意义的问题,因为在很多情况下,你可能并不会混合使用这四种处理机制。我想,最重要的问题还是在如何选用这些错误处理机制上。对于这个问题,希望有经验的朋友能够谈谈看法。

好了,关于ASP.NET的四种错误处理机制就介绍到这里,也该说说自己的一些感受了。ASP.NET的设计者确实站在开发者的角度作了周全的考虑,因此提供了多达四种的错误处理机制供我们选用,这一点是值得称道的。但是套用一句广告词——多则惑,我们也会被这么多的错误处理机制弄得有些头晕。对照J2EE领域中的错误处理,我们可以发现会相对简单一些。首先是对应<customErrors>的设置,我们也可以从J2EE项目最常用的web.xml文件中找到类似的配置项:<errorPage>;其次,在J2EE的领域中,Page并不是一个重要的实体而且事件驱动模型也不是必需的,所以我还真的找不到与Application_ErrorPage_Error方法对应的处理机制;最后,在J2EE的领域中,更多强调的是RequestResponse,一旦在逻辑处理中出现了错误,我们可以很容易地通过RequestDispatcherRequest分发到相应的错误处理模块中,事实上这是非常灵活的一种处理方式,有兴趣的朋友不妨了解一下。
//==========================================================
 

ASP.NET的自定义错误处理类

/*----------------------------------------------------------------

 * Copyright (C) BYD-D3IT, all rights reserved.

*

 * 文件名 :ErrorManager.cs

 * 功能描述:asp.net中统一的错误修理,与本类相配套需要增加一个错误信息显示页面,如error.aspx 

 *

 * 使用说明:. 在Application_Start()中启动定时器(定时清空错误信息):ErrorManager.Instance.Start(),

 *              默认小时运行一次,或用ErrorManager.Instance.SetTimerInterval()设置。

 *           2. 在Application_Error()中,当发生错误时,保存这个错误信息并转到error.aspx中显示这个错误

 *               string key = ErrorManager.Instance.AddError();

 *               Response.Redirect("error.aspx?key=" + key);

 *           3. 在error.aspx中通过url传来的key,取得并显示错误信息:

 *               string err = ErrorManager.Instance.GetError(key)

 *              err中前个字符是错误发生的时间,后面是错误信息。

 *           4. 为了捕捉Session超时的错误,而不是返回Session[key]是null的错误信息,本类增加了GetSession()

 *              和SetSession函数来统一管理Session,以后aspx中不能直接读取Session,而必须通过本类来读取。

 *

 *

 * 创建标识:

 *

 * 修改标识:

 * 修改描述:

 *

 * 修改标识:

 * 修改描述:

 *----------------------------------------------------------------*/

using System;

using System.Data;

using System.Configuration;

using System.Web;

using System.Web.Security;

using System.Web.UI;

using System.Web.UI.WebControls;

using System.Web.UI.WebControls.WebParts;

using System.Web.UI.HtmlControls;

using System.Collections;

///<summary>

/// Summary description for Error

///</summary>

public class ErrorManager

{

    private System.Timers.Timer m_timer;

    private Hashtable m_htErr;

    /**////<summary>

    ///私有的构造函数

    ///</summary>

    private ErrorManager()

    {

        this.m_timer = new System.Timers.Timer();

        this.m_timer.Enabled = false;

        this.m_timer.Interval = 12 * 60 * 60 * 1000;    //默认个小时执行一次

        this.m_timer.Elapsed += new System.Timers.ElapsedEventHandler(m_timer_Elapsed);

        this.m_htErr = new Hashtable();

    }

    /**////<summary>

    ///单例模式的接口

    ///</summary>

    public static readonly ErrorManager Instance = new ErrorManager();

    /**////<summary>

    ///设置定时器的频率,单位是毫秒

    ///</summary>

    ///<param name="Interval">毫秒</param>

    public void SetTimerInterval(int Interval)

    {

        this.m_timer.Interval = Interval;

    }

    /**////<summary>

    ///定时器开始

    ///</summary>

    public void TimerStart()

    {

        this.m_timer.Enabled = true;

    }

    /**////<summary>

    ///定时器结束

    ///</summary>

    public void TimerStop()

    {

        this.m_timer.Enabled = false;

    }

    /**////<summary>

    ///发生了一个错误,把错误信息保存起来,并返回错误的id,便于页面中读取

    ///</summary>

    ///<returns>返回错误的id</returns>

    public string AddError()

    {

        string key = Guid.NewGuid().ToString();

        string msg = System.DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")

            + HttpContext.Current.Server.GetLastError().GetBaseException().Message;

        this.m_htErr.Add(key, msg);

        HttpContext.Current.Server.ClearError();

        return key;

    }

    ///<summary>

    ///返回指定Key的错误信息,前个字符是错误发生的时间

    ///</summary>

    ///<param name="key">key,是一个guid</param>

    ///<returns>返回错误信息</returns>

    public string GetError(string key)

    {

        return this.m_htErr[key].ToString();

    }

    /**////<summary>

    ///定时在Hashtable中清理错误信息

    ///</summary>

    ///<param name="sender"></param>

    ///<param name="e"></param>

    private void m_timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)

    {

        ArrayList list = new ArrayList();

        lock (this.m_htErr)

        {

            DateTime now = DateTime.Now;

            TimeSpan ts;

            foreach (string key in this.m_htErr.Keys)

            {

                //前个字符是错误发生的日期,yyyy-MM-dd HH:mm:ss

                string time = this.m_htErr[key].ToString().Substring(0, 19);   

                ts = now - Convert.ToDateTime(time);

                if (ts.TotalMinutes > 20)   //把分钟前的错误信息从hashtable中清除

                    list.Add(key);

            }

            foreach (string key in list)

            {

                this.m_htErr.Remove(key);

            }

        }

    }

    #region Session操作的封装

    /**////<summary>

    ///取得指定键值的Session

    ///</summary>

    ///<param name="key">键值</param>

    ///<returns>键内容值</returns>

    public object GetSession(string key)

    {

        object val = HttpContext.Current.Session[key];

        if (val == null)

            throw new Exception("页面超时,请重新登录。");

        return val;

    }

    /**////<summary>

    ///设置Session

    ///</summary>

    ///<param name="key">键值</param>

    ///<param name="val">键内容</param>

    public void SetSession(string key, object val)

    {

        HttpContext.Current.Session[key] = val;

    }

    #endregion

}