第15章 ASP.NET异常处理
任何软件都有出现异常的可能,网站在运行过程中,也难免会遇到错误。在ASP.NET中如何处理异常是很重要的处理事项。页面一旦发生错误,该错误不应让用户看到,而应在后台进行异常处理。本章将给大家详细讲解ASP.NET中的异常处理机制。
15.1 异常处理基础
在开发.NET软件时,使用结构化异常处理方式进行编码设计是非常有必要的。对于ASP.NET应用程序来说,错误和异常处理是开发安全强固的应用程序重要的一步。本节我们来学习异常处理的一些基础知识。
15.1.1 异常处理简介
知识点讲解:光盘\视频讲解\第15章\异常处理简介.wmv
大多数.NET语言都支持结构化异常处理。当一个错误引发时,.NET框架创建一个异常对象用于呈现问题,开发人员可以使用异常处理器来捕捉这个异常对象。如果不使用异常处理器,用户代码会被中断,ASP.NET将显示一个让用户不知所措的错误处理页面。
1. 什么是异常
异常表示在应用程序执行期间发生的错误,以及其他的意外行为。
2. 导致异常的原因
在网站运行时,发生异常的原因是多种多样的。有一些错误是由于开发人员的疏漏造成的,如除数为0。一般情况下,导致异常的原因有以下几种:
(1)代码或调用的代码中有错误。在执行语句和表达式的过程中,有时会出现一些例外情况,该种情况使得某些操作无法正常完成,此时会引发一个异常。
(2)应用程序中使用throw语句抛出一个异常,此时会引发一个异常。
(3)操作系统资源不可用。
(4)公共语言运行库遇到意外情况。
注意:有些异常是可以恢复的,而有一些则不能。
3. 异常未捕获的结果
一般情况下,如果异常没有被应用程序捕获,那么.NET运行时系统就会捕获该异常。
【示例15-1】下面演示的是一个被零除的异常未被捕获的例子。
(1)创建一个网站,命名为“异常未被捕获的结果示例”。
(2)在该网站中添加一个Web页面,命名为Default.aspx。在该页面中添加一个Button控件和一个Label控件,并为按钮添加单击事件。Default.aspx页面的代码如下:
示例15-1:异常未被捕获的结果
源码路径:光盘\源文件\Chapter15\示例15-1\异常未被捕获的结果示例\Default.aspx
在Web页面的代码后置文件中为Button控件添加单击事件。Default.aspx文件的后置代码具体如下:
示例15-1:异常未被捕获的结果
源码路径:光盘\源文件\Chapter15\示例15-1\异常未被捕获的结果示例\Default.aspx.cs
运行应用程序,出现如图15.1所示现象。
图15.1 异常信息
在该示例中,运行应用程序时,系统会终止程序的运行,并弹出15.1所示的对话框。该对话框中说明了引发的异常类和相关的异常描述。很明确地告诉开发人员引发异常的原因是“尝试除以零”。
15.1.2 异常类
知识点讲解:光盘\视频讲解\第15章\异常类.wmv
当异常抛出时,.NET CLR(Common Language Runtime,公共语言运行库)会捕获出现的异常。异常本身不再使用一些常量或者特定的字符串表示特定的错误信息。.NET将所有类型的异常都定义为从Exception类中派生的异常类,即Exception类是所有异常类的基类。
1. 基类Exception
大多数的异常对象都是Exception类的某个派生类的实例。从Object类派生的对象也可以作为异常进行引发。不是从Exception类派生的对象不容易被捕捉,因此建议开发应用程序时使用从Exception派生的对象。该类包含了一些与异常相关的属性,这些属性可以让用户更容易理解异常的信息。Exception类的成员如表15.1所示。
表15.1 Exception类的属性成员描述
System.Exception是所有其他异常类的基类。在Exception类之后,又划分了两种类型的异常:
- 从SystemException派生的预定义CLR异常类。
- 从ApplicationException派生的用户定义的应用程序异常类。
这两种类型的异常构成了几乎所有运行库异常的基础。在.NET体系结构中,所见到的大多数异常都是它们中的一种。如果要定义自己的异常类或者建立自定义的异常体系,需要从ApplicationException类中派生。
2. 常用的异常类
SystemException类中的异常是CLR预定义的异常类。一些常用的.NET预定义异常类的介绍见表15.2介绍。
注意:更多的异常类信息可以参考.NET中的文档。
表15.2 .NET预定义的异常类
Visual Studio 2012提供了一个工具用于浏览这些异常。选择主菜单的“调试”|“异常”命令,将弹出一个“异常”对话框,如图15.2所示。
图15.2 “异常”对话框
从图15.2可以看到,“异常”对话框中列出了很多的异常类型。Common Language Runtime Exceptions节点是.NET中的异常层次结构。“异常”对话框允许开发人员指定调试时异常是被程序代码处理还是由Visual Studio 2012切换到中断模式。当在“引发”栏中选择某个异常,一旦出现异常,Visual Studio 2012则进入中断模式。如果未在“引发”栏中选择该异常,则程序代码继续执行,将在Label控件中显示异常的信息。
15.2 处理异常
开发人员在编写程序时,可以充分利用.NET提供的功能强大的结构化异常处理机制。该机制可以在代码中捕获应用程序的异常,并进行相应的处理。异常处理需要用4个关键字:try、catch、throw和finally,它们构成了一个相关的子系统。在这个子系统中,一个关键字的使用暗含着另一个关键字的使用。本节我们来学习处理异常的方式。
15.2.1 使用try/catch语句
知识点讲解:光盘\视频讲解\第15章\使用try/catch语句.wmv
本书采用的ASP.NET开发语言是C#,C#中异常处理的核心是try和catch关键字。这两个关键字必须配套使用,不能只使用try而不使用catch,也不能只使用catch而不使用try。try块中包含可能导致异常的保护代码,该块一直执行到引发异常或成功完成为止。try/catch异常处理块的通用形式如图15.3所示。
图15.3 try/catch语句通用形式
【示例15-2】下面演示的是使用try/catch语句捕捉异常的方法。
(1)创建一个网站,命名为“使用try语句捕捉异常”。
(2)在该网站上添加一个Web页面,命名为Default.aspx。在该Web页面上添加一个Button控件和一个Label控件,并为该按钮控件添加单击事件。Default.aspx页面的代码如下:
示例15-2:使用try语句捕捉异常
源码路径:光盘\源文件\Chapter15\示例15-2\使用try语句捕捉异常\Default.aspx
在Web页面的代码后置文件中为Button控件添加单击事件。Default.aspx文件的后置代码具体如下:
示例15-2:使用try语句捕捉异常
源码路径:光盘\源文件\Chapter15\示例15-2\使用try语句捕捉异常\Default.aspx.cs
运行结果如图15.4所示。
图15.4 运行结果图
在该示例中,将可能出现异常的代码包含在try块中。当出现“尝试除以零”的异常时,将异常信息显示在Label控件上。
注意:(1)希望监控是否会出现错误的代码包含在try块。
(2)出现异常时,try块将抛出一个异常,catch块则负责捕获该异常。此时,try块终止,控制权传递给catch块(这里不是调用catch块,而是把程序的执行转向它)。将k值显示在Label控件上的语句将不执行。执行catch块之后,程序控制权交给catch块之后的语句。因此,由异常处理程序完成对异常代码的处理,从而使得程序能正常执行。
15.2.2 多条catch语句
知识点讲解:光盘\视频讲解\第15章\多条catch语句.wmv
一个try块可以与多条catch子句相关联。但是,每条catch子句必须分别捕获不同类型的异常。使用多条catch语句的运行流程如图15.5所示。
图15.5 使用多条catch语句
【示例15-3】下面演示的是使用多条catch语句的方法。
(1)创建一个网站,命名为“使用多条catch语句捕捉异常”。
(2)在该网站上添加一个Web页面,命名为Default.aspx。在该Web页面上添加一个Button控件和一个Label控件,并为该按钮控件添加单击事件。Default.aspx页面的代码如下:
示例15-3:使用多条catch语句捕捉异常
源码路径:光盘\源文件\Chapter15\示例15-3\使用多条catch语句捕捉异常\Default.aspx
在Web页面的代码后置文件中为Button控件添加单击事件。Default.aspx文件的后置代码具体如下:
示例15-3:使用多条catch语句捕捉异常
源码路径:光盘\源文件\Chapter15\示例15-3\使用多条catch语句捕捉异常\Default.aspx.cs
运行结果如图15.6所示。
图15.6 运行结果图
注意:在该示例中,使用两条catch语句捕捉不同的异常。第1个catch子句中捕捉的是除数为零的异常,第2个catch子句中捕捉的是越界的异常。分别将正常执行的表达式、结果和异常信息显示在Label控件上。
15.2.3 finally语句
知识点讲解:光盘\视频讲解\第15章\finally语句.wmv
使用finally块可以清除try中分配的任何资源,以及运行任何即使在发生异常时也必须执行的代码。代码控制权最终总是传递给finally块,与try块的退出方式无关。因此,finally块提供了一种保证资源清理或者是资源恢复的机制。finally语句的通用形式如图15.7所示。
图15.7 finally语句通用形式
【示例15-4】下面演示的是finally语句的使用方法。
(1)创建一个网站,命名为“finally语句的使用方法”。
(2)在该网站中添加一个Web页面,命名为Default.aspx。在该页面中添加一个Button控件和一个Label控件,并为按钮添加单击事件。Default.aspx页面的代码如下:
示例15-4:finally语句的使用方法
源码路径:光盘\源文件\Chapter15\示例15-4\finally语句的使用方法\Default.aspx
在Web页面的代码后置文件中为Button控件添加单击事件。Default.aspx文件的后置代码具体如下:
示例15-4:finally语句的使用方法
源码路径:光盘\源文件\Chapter15\示例15-4\finally语句的使用方法\Default.aspx.cs
运行结果如图15.8和图15.9所示。
图15.8 运行结果图(1) | 图15.9 运行结果图(2) |
在该示例中,捕获的异常是越界异常。当在for循环中将循环变量设定为0~9时,超出了数组的范围。因此,会引发一个数组越界的异常。
注意:从示例中可以看出,无论程序是什么结果,finally块的代码总是会执行的。
15.2.4 嵌套try块
知识点讲解:光盘\视频讲解\第15章\嵌套try块.wmv
一个try块能够嵌套在另一个try块中。内层try块中产生的异常如果未被相关联的catch子句捕获,就会传递到外层try块中。当发生异常时,就寻找相关联的catch语句,如图15.10所示。如果到达调用堆栈顶部仍然没有找到处理该异常的catch块,则由默认的异常处理程序处理该异常,然后应用程序终止。
图15.10 嵌套try块
【示例15-5】下面演示的是嵌套try块的用法。
(1)创建一个网站,命名为“使用嵌套的try块”。
(2)在该网站中添加一个Web页面,命名为Default.aspx。在该页面中添加一个Button控件和一个Label控件,并为按钮添加单击事件。Default.aspx页面的代码如下:
示例15-5:使用嵌套的try块
源码路径:光盘\源文件\Chapter15\示例15-5\使用嵌套的try块\Default.aspx
在Web页面的代码后置文件中为Button控件添加单击事件。Default.aspx文件的后置代码具体如下:
示例15-5:使用嵌套的try块
源码路径:光盘\源文件\Chapter15\示例15-5\使用嵌套的try块\Default.aspx.cs
运行结果如图15.11所示。
图15.11 运行结果图
在该示例中,使用了嵌套的try块。在内层的try块中捕获的是DivideByZeroException异常,即除数为零的异常。外层try块中捕获的是IndexOutOfRangeException异常,即数组越界的异常。在内层循环中只能捕获除数为零的异常。当引发的异常类型是IndexOutOfRangeException异常时,将跳到外层try块的catch语句中,执行相关操作。
注意:try块的嵌套尽量不要过多。
15.2.5 重新抛出异常
知识点讲解:光盘\视频讲解\第15章\重新抛出异常.wmv
重新抛出异常是为了能让多个处理程序访问它。在重新抛出异常时,只需要直接使用throw语句,而不必指定异常,形式如图15.12所示。
图15.12 throw语句形式
【示例15-6】下面演示的是如何在程序中重新抛出异常。
(1)创建一个网站,命名为“抛出异常throw语句”。
(2)在该网站中添加一个Web页面,命名为Default.aspx。在该页面中添加一个Button控件和一个Label控件,并为按钮添加单击事件。Default.aspx页面的代码如下:
示例15-6:抛出异常throw语句
源码路径:光盘\源文件\Chapter15\示例15-6\抛出异常throw语句\Default.aspx
在Web页面的代码后置文件中为Button控件添加单击事件。Default.aspx文件的后置代码具体如下:
示例15-6:抛出异常throw语句
源码路径:光盘\源文件\Chapter15\示例15-6\抛出异常throw语句\Default.aspx.cs
运行结果如图15.13所示。
图15.13 运行结果图
注意:在该示例中,在方法f()中引发数组越界异常,处理异常后,使用throw语句将该异常重新抛出。在Button控件的单击事件中调用方法f(),重新抛出的越界异常在单击事件的catch块中被捕捉。因此数组越界异常被两次捕获。
15.3 自定义异常类
知识点讲解:光盘\视频讲解\第15章\自定义异常类.wmv
如果系统提供的异常类已经不能满足应用系统开发的需要,或者开发团队需要一套自定义的异常处理机制,可以自定义异常类。本节将给大家讲解如何自定义异常类。
自定义异常类非常简单,该类应直接或间接地继承自ApplicationException类。自定义异常类应该有良好的命名,建议命名规则为“错误描述+Exception”。自定义异常类应该定义三个构造函数:默认构造函数、接受错误信息的构造函数以及接受错误信息和内部异常对象的构造函数。在设计自定义异常类时,还需要遵循一些规则,具体描述如下:
(1)避免使用深的异常层次结构。
(2)要从.NET的异常基类中派生自定义的异常。
(3)异常必须可序列化,才能跨越应用程序域和远程处理边界来正确工作。
自定义异常类的通用形式如图15.14所示。
图15.14 自定义异常类
【示例15-7】下面演示的是如何自定义异常类。
(1)创建一个网站,命名为“自定义异常类的方法”。
(2)在该网站上添加一个类,命名为DivideException.cs。在该类中添加代码如下所示:
示例15-7:自定义异常类的方法
源码路径:光盘\源文件\Chapter15\示例15-7\自定义异常类的方法\App_Code\DivideException.cs
(3)在该网站中添加一个Web页面,命名为Default.aspx。在该页面中添加一个Button控件和一个Label控件,并为按钮添加单击事件。Default.aspx页面的代码如下:
示例15-7:自定义异常类的方法
源码路径:光盘\源文件\Chapter15\示例15-7\自定义异常类的方法\Default.aspx
在Web页面的代码后置文件中为Button控件添加单击事件。Default.aspx文件的后置代码具体如下:
示例15-7:自定义异常类的方法
源码路径:光盘\源文件\Chapter15\示例15-7\自定义异常类的方法\Default.aspx.cs
运行结果如图15.15所示。
图15.15 运行结果图
在该示例中,自定义了一个异常类DivideException,并为该异常类创建了3个构造函数。在Button控件的单击事件中,使用throw语句抛出了一个异常。此处调用的是异常类DivideException的默认构造函数。因此,在Label控件上显示文本“注意,不要让除数为零!”。
15.4 事件日志
当异常产生时,除了给客户端友好的提示外,对于服务器端的管理员来说,最好能够记录错误信息。.NET提供了多种日志工具,如产生的错误信息可以通过电子邮件发送、添加到数据库记录或者写入日志文件中。其中,最安全的方式是使用事件日志查看器。本节将详细介绍事件日志查看器。
15.4.1 事件查看器
知识点讲解:光盘\视频讲解\第15章\事件查看器.wmv
打开Windows的事件查看器,可以选择“开始”|“控制面板”|“系统和安全”|“管理工具”命令,然后双击打开“事件查看器”窗口。在“事件查看器”窗口左侧单击“Windows日志”左侧的小三角,在展开的选项中选择“应用程序”选项,如图15.16所示。
图15.16 事件查看器
注意:笔者这里使用的操作系统为Windows 7。
在默认情况下,事件查看器的应用程序会有五种类型的日志记录事件:
(1)应用程序日志。包含由应用程序或系统程序记录的事件。
(2)安全日志。记录诸如有效或无效的登录尝试等事件,以及记录与资源使用相关的事件。
(3)Setup日志。配置为域控制器的计算机将在此处显示其他日志。
(4)系统日志。包含Windows系统组件记录的事件。
(5)转发事件日志。这些事件通过其他计算机转发到此日志。
根据操作系统版本的不同,事件查看器中包含的事件日志项有所不同。在应用程序中第一个事件上单击右键,选择“属性”命令,弹出“事件属性”对话框,如图15.17所示。
图15.17 事件属性
在“事件属性”对话框中,可以看到事件的“来源”、“记录时间”和“事件ID”等信息。“常规”选项卡列出了事件的描述性信息。其中事件的类型又可以分为5种,具体描述如下。
- 信息:描述了应用程序、驱动程序或服务的成功操作的事件。例如,当网络驱动程序加载成功时,将会记录一个“信息”事件。
- 警告:不一定很重要,但将来可能导致问题的事件。例如,当磁盘空间不足时,便会记录“警告”事件。
- 错误:重要的问题,如数据丢失或功能丧失。例如,如果在启动过程中某个服务加载失败,将会记录“错误”事件。
- 审核成功:成功的任何已审核的安全事件。例如,用户试图登录系统成功会被作为“审核成功”事件记录。
- 审核失败:失败的任何已审核的安全事件。例如,如果用户访问网络驱动器失败,则该失败信息将作为“审核失败”事件记录。
15.4.2 写入事件日志
知识点讲解:光盘\视频讲解\第15章\写入事件日志.wmv
ASP.NET中的System.Diagnostics命名空间下提供了可以读写事件日志的类。开发人员可以使用这个命名空间的类RventLog将异常信息写入事件日志中。本小节我们来学习如何写入事件日志。
如果要编程写入事件日志,开发人员需要实现的步骤如下:
(1)创建EventLog实例。
(2)设置EventLog的Source属性为事件源。可以是任何字符串值,但通常以项目作为名称。
(3)调用EventLog对象的WriteEnty()方法将日志写入事件日志文件中。
而要成功地将日志写入事件日志文件则必须满足一些条件,具体如下:
(1)Source指定的事件源必须注册到写入的日志中,即必须指定Source属性,且要判断指定的源是否已经注册到正在写入的事件日志中。如果没有注册,ASP.NET会自动调用CreateEventSource()方法来创建源。
(2)消息的长度不能超过16KB。
(3)应用程序应该对尝试写入的日志具有访问权。
【示例15-8】下面演示的是如何编程写入事件日志。
(1)创建一个网站,命名为“写入事件日志”。
(2)在该网站中添加一个Web页面,命名为Default.aspx。在该页面中添加一个Button控件和一个Label控件,并为按钮添加单击事件。Default.aspx页面的代码如下:
示例15-8:写入事件日志
源码路径:光盘\源文件\Chapter15\示例15-8\写入事件日志\Default.aspx
在Web页面的代码后置文件中为Button控件添加单击事件,并编写两个方法f()和w()。f()方法用来抛出一个异常,w()方法是一个自定义写入事件日志的方法。Default.aspx文件的后置代码具体如下:
示例15-8:写入事件日志
源码路径:光盘\源文件\Chapter15\示例15-8\写入事件日志\Default.aspx.cs
运行结果如图15.18所示。
图15.18 运行结果图
在该示例中,专门定义了一个用于写入事件日志的方法w()。该方法有两个位置引用,具体说明如下:
(1)触发异常后,在catch语句中调用方法w(),此时将事件日志类型设置为“错误”。
(2)在finally语句块中调用方法w()。该语句块用于记录代码执行的时间。因此指定的事件类型为“信息”。
注意:事件类型是通过EventLogEntryType枚举类型来指定的。该枚举类型有5个可选值,具体如下。
- Error:错误。指示用户应该知道的严重问题。
- Warning:警告。指示并不具有重要性的问题,但此问题可能表示将来会导致问题的条件。
- Information:信息。指示重要、成功的操作。
- SucccessAudit:审核成功。指示当审核访问尝试成功时发生的安全事件。
- FailureAudit:审核失败。指示当审核访问尝试失败时发生的安全事件。
运行完该程序,打开“事件查看器”窗口,可以看到增加了如图15.19所示的两个事件日志项。
图15.19 查看事件日志项
在图15.19中还可以看到事件的属性,如“日志名称”、“来源”、“事件ID”和“级别”等相关信息。
15.4.3 自定义事件日志项
知识点讲解:光盘\视频讲解\第15章\自定义事件日志项.wmv
在编程中,还可以使用EventLog对象自定义事件日志项。自定义的日志是指属于自己分类的日志,例如可以创建一个属于本程序特有的错误分类。本小节将通过示例详细讲解如何自定义事件日志项。
【示例15-9】下面演示的是如何自定义事件日志项。
(1)创建一个网站,命名为“自定义事件日志项”。
(2)在该网站中添加一个Web页面,命名为Default.aspx。在该页面中添加一个Button控件和一个Label控件,并为按钮添加单击事件。Default.aspx页面的代码如下:
示例15-9:自定义事件日志项
源码路径:光盘\源文件\Chapter15\示例15-9\自定义事件日志项\Default.aspx
在Web页面的代码后置文件中为Button控件添加单击事件,并编写两个方法f()和w()。f()方法用来抛出一个异常,w()方法是一个自定义写入事件日志的方法。在w()方法中添加了一个logname,表示事件日志的名称。然后使用EventLog.SourceExists()方法判断事件源是否存在。如果不存在,则调用CreateEventSource()方法创建事件源。Default.aspx文件的后置代码具体如下:
示例15-9:自定义事件日志项
源码路径:光盘\源文件\Chapter15\示例15-9\自定义事件日志项\Default.aspx.cs
运行结果如图15.20所示。
图15.20 运行结果图
在该示例中,在w()方法中添加了一个logname参数,表示事件日志的名称。然后使用EventLog.SourceExists()方法判断事件源是否存在。如果不存在,则调用CreateEventSource()方法创建事件源。
注意:运行程序后,打开“事件查看器”窗口,可以看到创建的ASP.NET 4.5日志项。单击该项,可以看到在这个项里面新增的事件日志,如图15.21所示。
图15.21 自定义事件日志项ASP.NET 4.5
15.4.4 动态查看事件日志
知识点讲解:光盘\视频讲解\第15章\动态查看事件日志.wmv
事件日志只能在服务器端的电脑上查看,若管理员不能时时在服务器端,可以通过编程动态查看事件日志。本小节将给大家介绍如何通过编程的方式动态查看事件日志。
【示例15-10】下面演示的是如何通过编程的方式动态查看事件日志。
(1)创建一个网站,命名为“动态查看事件日志”。
(2)创建一个代表日志信息的Program类,该类代表每一条日志信息。Program类的代码如下所示:
示例15-10:动态查看事件日志
源码路径:光盘\源文件\Chapter15\示例15-10\动态查看事件日志\App_Code\Program.cs
为了更好地获取事件日志信息,在该网站中创建一个用于获取事件日志的类List,该类将获取指定的事件日志名称下的所有事件日志。
示例15-10:动态查看事件日志
源码路径:光盘\源文件\Chapter15\示例15-10\动态查看事件日志\App_Code\Program.cs
(3)在该网站中添加一个Web页面,命名为Default.aspx。在该页面中添加一个GridView控件。Default.aspx页面的代码如下:
示例15-10:动态查看事件日志
源码路径:光盘\源文件\Chapter15\示例15-10\动态查看事件日志\Default.aspx
在Web页面的Page_Load事件中添加代码,将事件日志显示在GridView控件上。Default.aspx文件的后置代码具体如下:
示例15-10:动态查看事件日志
源码路径:光盘\源文件\Chapter15\示例15-10\动态查看事件日志\Default.aspx.cs
运行结果如图15.22所示。
图15.22 运行结果图
注意:在该示例中,Program类用于获取日志的类型、日志消息、日志生成的时间和日志来源。List类将保存指定日志名称的所有日志。使用foreach语句遍历EventLog对象。最终,将所有ASP.NET 4.5下的事件日志显示在GridView控件中。
15.5 页面追踪
在应用程序的开发和调试阶段,获取详细的错误信息对应用程序非常有帮助。有时应用程序可能生成了一些无效的数据,但并没有明显的异常触发。ASP.NET提供了页面追踪功能,让开发人员使用一种更方便、更有弹性的方式来报告诊断信息。本节我们来学习页面追踪的相关知识。
15.5.1 启用页面追踪
知识点讲解:光盘\视频讲解\第15章\启用页面追踪.wmv
默认情况下,ASP.NET的页面追踪功能并未打开,需要开发人员显式地启用它。启用页面追踪通常有两种方式,具体如下:
(1)在Web页面的源代码的@Page指令区中启用页面追踪。示例代码如下:
(2)在Web页面的后置代码文件中的Page_Load事件中启用。Page类提供了一个Trace属性,该属性是一个TraceContext类的对象。可以使用Trace的IsEnabled属性来开启页面追踪。示例代码如下:
注意:使用编程的方式启用或者禁用页面追踪功能,便于开发人员根据特定的上下文环境来启用或者禁用该功能。
【示例15-11】创建一个网站,命名为“启用页面追踪”。在该网站上添加一个Web页面,命名为Default.aspx。当启用了页面追踪后,ASP.NET页面会显示很多的跟踪信息,如图15.23所示。
图15.23 页面追踪
开发人员还可以使用TraceMode属性指定追踪消息出现的顺序,该属性有两种可选值,介绍如下。
- SortByTime:按照追踪消息的处理顺序进行排序。
- SortByCategory:按照在服务器控件的Trace.Warn()和Trace.Write()方法调用中指定的类别对消息进行排序。
示例代码如下:
示例15-11:启用页面追踪
源码路径:光盘\源文件\Chapter15\示例15-11\启用页面追踪\Default.aspx.cs
15.5.2 页面追踪信息
知识点讲解:光盘\视频讲解\第15章\页面追踪信息.wmv
ASP.NET的页面追踪信息包含多种分类。根据页面状态的不同,开发人员可能看不到所有的追踪信息。本小节详细介绍ASP.NET中的追踪分类信息。
1. 请求详细信息
请求详细信息包括了会话ID、请求类型和请求时间等基本信息,如图15.24所示。
图15.24 请求详细信息
2. 跟踪信息
跟踪信息显示在页面被发送到客户端之前,显示了页面处理流程中不同的处理情形,并且提供了页面执行的详细执行时长,如图15.25所示。
图15.25 跟踪信息
3. 控件树
控件树用于显示页面上所有的控件,以缩放的形式显示控件的层次结构,所有的控件都定义在Page标签中。ASP.NET同时对空白和其他静态页面元素自动添加了Literal控件进行显示,如图15.26所示。
图15.26 控件树
4. 会话状态和应用程序状态
该项用于显示当前应用程序中会话状态和应用程序状态集合中的每项。在Web页面中添加如下代码块:
代码块中在Session和Application集合中各自添加了一个值为se的状态。运行程序,可以在页面追踪中看到这两个状态项,如图15.27所示。
图15.27 会话状态和应用程序状态
5. 请求Cookie集合和响应Cookie集合
请求Cookie集合和响应Cookie集合显示发送到Web浏览器的请求Cookie信息和Web服务器返回的响应Cookie信息。
6. 标头集合和响应标头集合
标头集合列出了所有的HTTP头信息。头信息是作为请求的一部分发送到Web服务器的一小块信息,包括请求信息、支持的内容类型等。“响应标头集合”列表列出了作为响应发送到客户端的一部分信息,如图15.28所示。
图15.28 标头集合和响应标头集合
7. 窗体集合
窗体集合列出了页面回送的信息,该信息包含所有由Web控件提交的值。
8. Querystring集合
Querystring集合列出所有查询字符串的名称和值。这些值也可以从URL中直接查看,但是如果查询字符串包含过多的信息,则在URL中难以明确详细信息,此时可以通过Querystring集合查看信息。
9. 服务器变量
服务器变量列出了所有的服务器端变量和内容,通常情况下不需要查看这些信息。如果需要编程控制服务器端变量,可以使用Request.ServerValiables集合。“服务器变量”集合部分内容如图15.29所示。
图15.29 服务器变量
注意:页面追踪信息包含多种分类。根据页面状态的不同,开发人员可能看不到所有的追踪信息。
15.5.3 编写自定义追踪信息
知识点讲解:光盘\视频讲解\第15章\编写自定义追踪信息.wmv
当开发人员在调试和跟踪应用程序时,可以借助于TraceContext类的Warn()或Write()方法编写自定义的跟踪。用Warn()方法编写的消息将显示为红色文字。使用这两个方法可以将自定义跟踪信息附加到ASP.NET页的尾端,或附加到跟踪日志中。
【示例15-12】下面演示的是如何编写自定义追踪信息。
(1)创建一个网站,命名为“编写自定义追踪信息”。
(2)在该网站中添加一个Web页面,命名为Default.aspx。在该页面中添加一个Button控件和一个Label控件,并为按钮添加单击事件。Default.aspx页面的代码如下:
示例15-12:编写自定义追踪信息
源码路径:光盘\源文件\Chapter15\示例15-12\编写自定义追踪信息\Default.aspx
当引发异常时,可以使用Warn()的3个参数重载方法将异常信息写入跟踪日志中。该方法有3个重载方式,具体说明如下。
- Warn(String):将跟踪消息写入跟踪日志。所有警告在日志中均显示为红色文本。
- Warn(String,String):将跟踪信息写入跟踪日志,包括任何用户定义的类别和跟踪消息。所有警告在日志中均显示为红色文本。
- Warn(String,String,Exception):将跟踪信息写入跟踪日志。包括用户定义的所有类别、跟踪消息和错误消息。所有警告在日志中均显示为红色文本。
在Web页面的代码后置文件中为Button控件添加单击事件。Default.aspx文件的后置代码具体如下:
示例15-12:编写自定义追踪信息
源码路径:光盘\源文件\Chapter15\示例15-12\编写自定义追踪信息\Default.aspx.cs
运行结果如图15.30所示。
图15.30 运行结果图
注意:在该示例中,单击“编写自定义追踪信息”按钮,抛出一个异常,并将该异常信息使用Trace.Warn()方法写入到跟踪日志中。然后在finally语句块中使用Trace.Write()方法写入程序执行完成的跟踪信息。
15.6 小结
本章主要介绍了ASP.NET的异常处理,包括异常处理基础、处理异常、自定义异常类、事件日志和页面追踪等。重点是对try/catch语句块、事件日志和页面追踪的掌握,有兴趣的读者可以自己尝试着运用这些异常处理方法解决自己在网站制作过程中遇到的问题。
15.7 本章习题
习题15-1 在Visual Studio 2012中新建一个网站,命名为chapter15_1。在该网站上添加一个Web页面,命名为Default.aspx。在Web页面上添加一个Button控件和一个Label控件。在Button控件的单击事件中使用try/catch语句,运行结果如图15.31所示。
图15.31 运行结果图
【分析】本题目主要考查的是对try/catch语句的掌握情况。
【关键代码】Default.aspx.cs文件代码如下:
public partial class _Default : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { } protected void Button1_Click(object sender, EventArgs e) { int[] n = new int[5]; Label1.Text = "数组n的元素如下:<br />"; try { for (int i=1; i < 10; i++) { n[i] = i; Label1.Text += n[i]+"<br />"; } } catch (IndexOutOfRangeException ex) { Label1.Text += "<br />错误信息如下:<br />" + ex.Message; } } }
习题15-2 在Visual Studio 2012中新建一个网站,命名为chapter15_2。在该网站上添加一个Web页面,命名为Default.aspx。在Web页面上添加一个Button控件和一个Label控件。在Button控件的单击事件中使用finally语句,运行结果如图15.32所示。
图15.32 运行结果图
【分析】本题目主要考查的是对finally语句的掌握情况。
【关键代码】Default.aspx.cs文件代码如下:
public partial class _Default : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { } protected void Button1_Click(object sender, EventArgs e) { int m = 55, n = 0; try { int q = m / n; Label1.Text = q.ToString(); } catch (DivideByZeroException ex) { Label1.Text += "<br />错误信息如下:<br /><br />" + ex.Message; } finally { Label1.Text += "<br /><br />finally语句一定会执行。"; } } }
习题15-3 在Visual Studio 2012中新建一个网站,命名为chapter15_3。在该网站上添加一个Web页面,命名为“Default.aspx”。在Web页面上添加一个Button控件和一个Label控件。在Button控件的单击事件中使用throw语句重新抛出异常,运行结果如图15.33所示。
图15.33 运行结果图
【分析】本题目主要考查的是对throw语句的掌握情况。
【关键代码】Default.aspx.cs文件代码如下:
public partial class _Default : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { } protected void Button1_Click(object sender, EventArgs e) { int[] m = { 8, 6, 7, 12 }; int[] n = { 4, 2, 1 }; try { try { Label1.Text = "运算结果如下:<br />"; for (int i = 0; i < m.Length; i++) { Label1.Text += m[i] / n[i] + "<br />"; } } catch (IndexOutOfRangeException ex) { Label1.Text += "<br />内层异常信息如下:<br />" + ex.Message; throw; } } catch (IndexOutOfRangeException ex) { Label1.Text += "<br /><br />外层异常信息如下:<br />" + ex.Message; } } }
习题15-4 在Visual Studio 2012中新建一个网站,命名为chapter15_4。在该网站上添加一个Web页面,命名为Default.aspx。在Web页面上添加一个Button控件和一个Label控件。在Button控件的单击事件中写入事件日志,运行结果如图15.34所示。打开事件查看器,可以看到如图15.35所示的结果。
图15.34 运行结果图(1)
图15.35 运行结果图(2)
【分析】本题目主要考查的是对写入事件日志的掌握程度。
【关键代码】Default.aspx.cs文件代码如下:
using System.Diagnostics; public partial class _Default : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { } protected void Button1_Click(object sender, EventArgs e) { try { string s = null; f(s); } catch (ArgumentNullException ex) { Label1.Text = "异常信息:<br /><br />" + ex.Message + "<br />"; w(ex.Message, "写入事件日志示例", EventLogEntryType.Error); } finally { Label1.Text += "<br /><br />当前程序的执行时间是:<br /><br />" + DateTime.Now.ToLongTimeString(); w("成功执行时间" + DateTime.Now.ToString (), "写入事件日志示例", EventLogEntryType.Information); } } protected void f(string s) { throw new ArgumentNullException(); } protected void w(string message, string source, EventLogEntryType entype) { EventLog eventLog = new EventLog(); eventLog.Source = source; eventLog.WriteEntry(message, entype); } }
习题15-5 在Visual Studio 2012中新建一个网站,命名为chapter15_5。在该网站上添加一个Web页面,命名为Default.aspx。在Web页面上添加一个Button控件和一个Label控件。在Button控件的单击事件中编写自定义追踪信息,运行结果如图15.36所示。
图15.36 运行结果图
【分析】本题目主要考查的是对启用页面追踪和编写自定义追踪信息的掌握情况。
【关键代码】Default.aspx.cs文件代码如下:
public partial class _Default : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { Trace.IsEnabled = true; } protected void Button1_Click(object sender, EventArgs e) { try { string s = null; f(s); } catch (ArgumentNullException ex) { Label1.Text = "异常信息:<br />" + ex.Message + "<br />"; Trace.Warn("值不能为空!", "Warn信息", ex); } finally { Label1.Text += "当前程序的执行时间是:<br />" + DateTime.Now.ToLongTimeString(); Trace.Warn("当前程序执行时间是:" + DateTime.Now.ToString()); } } protected void f(string s) { throw new ArgumentNullException(); } }