帝国软件
  设为首页 加入收藏 关于我们
 
解密帝国网站管理系统
栏 目:
 
您的位置:首页 > 技术文档 > ASP编程
错误的种类
作者:佚名 发布时间:2005-04-02 来源:不详
 

 前面已经介绍了使用ASP所需要的基本技能,本章要讨论的另外一个问题是当ASP出现错误时怎么办,ASP出错时是什么情况。当精心编排的ASP页面出现问题停止了执行时,用户一般得到的仅是一些用处不大的建议,诸如:点击“刷新”按钮,或者“与站点的Web管理员联系,告诉他们你的页面不能正常工作了”等等。
本章除了提供有用的信息外,还想提供一个帮助区域。我们将详细介绍在脚本和页面中错误如何出现,可能产生的错误类型,以及什么造成了这些错误。更重要的是,要讨论如何尽可能避免错误的发生,如不能避免又如何妥善处理。
因此,本章将要探讨页面调试技术,也就是如何花费不多的精力和时间就能找到错误并解决问题。
本章包括以下内容:
· 能够出现的错误类型。
· 如何防止各种错误的产生。
· 如果不能防止错误发生,如何妥善处理这些错误。
· 如何发现和处理脚本错误及其他类型的错误。
· 如何使用定制的错误页面得到错误信息。
· 如何记录发生的错误以监视我们的网站。
· 创建一个定制错误网页和一个错误日志文件。
· 提供相关的在线帮助。
本章不涉及如何处理使用ActiveX Data Objects(ADO)访问数据源时出现的各种特殊类型的错误。像许多组件一样,ADO提供了自己的错误处理系统,第8章将深入讨论这一点。本章将从讨论能出现的各种错误类型开始,使我们能够认识这些错误并采取相应的措施。
据说,在非洲最黑暗的雨林深处,有这样一群程序员,他们的程序代码从来没有出现过错误。但是,很遗憾他们从没有享受过调试一段不能正常工作的应用程序的乐趣。调试程序代码是一个真正充满快乐的工作,所以我们要面对这个问题,在调试程序的过程中检验我们的观察力和横向思维能力。大多数“真实世界”的程序员能够体验这些乐趣是一件好事。
当然,有些人会说,调试程序与其说是判断,不如说是碰运气。花费了许多时间去调试一段有错误的程序,在某种程度上可以说确实是依赖运气。但是,如果第一步从合适的地方开始查看,可能会更快地解决问题。
但这不是程序调试应采取的办法。从理论上讲,当某段程序运行失败时,应该以逻辑或顺序方式跟踪错误。作为一个聪明和有经验的程序员,这才是调试时常用的方法,只有业余人员才随意改变程序中变量的值,到处添加Response.Write语句进行调试。
然而,为了能够在逻辑上跟踪程序中的错误,必须了解有关错误如何出现方面的基础知识,更重要的是知道错误出现在哪里,以便很快就能找到相应的地方。本章讨论的内容是有关程序中能够出现的不同种类的错误,错误的不同表现,以及如何记录和排除这些错误。同样重要的是,还将介绍如何避免这些错误的发生。
本章将从介绍可能出现的不同种类的错误开始,如果认为你的代码不会出现任何错误,可以直接跳到下一章。

7.1 错误的种类

7.1.1 语法或“编译”错误
       当我们第一次运行新编写的程序代码时,通常看到的第一种错误类型是“syntax error”。这就是所说的,程序代码上的语法错误。这就像在写作中使用了错误的语法,使读者不能了解其中的含义。而解释器(诸如脚本引擎)和编译器对语法要求得更加严格和准确。
       语法错误通常也是最早出现和需要排除的。大多数情况下,解释器和编译器会指出行号和所在行中的字符位置,以及在相应的位置上缺少的内容。下面举一个简单的例子,如下所示的这样一段程序:
       <%
       Response.Write "The repayments for your loan are $" & chrPayment _
                            & " per " & strInterval & , due on the " &strDay & " of each "
                            & strInterval & "."
       %>
       我们希望得到下面的结果:
       The repayments for your loan are $124.50 per month, due on the 12th of each month.
       实际上得到的结果如图7-1所示:

       豆豆注:
              如果你的错误提示信息无法出现“语法错误”,请将你的WINNTHelpiisHelpcommon500-100.asp做如下改变(加了两行黑体字):
  ...
  Dim bakCodepage
  bakCodepage = Session.Codepage
  Session.Codepage = 936
  Response.Write Server.HTMLEncode(objASPError.Category)
  If objASPError.ASPCode > "" Then Response.Write Server.HTMLEncode(", " & objASPError.ASPCode)
  Response.Write Server.HTMLEncode(" (0x" & Hex(objASPError.Number) & ")" ) & "<br>"

  If objASPError.ASPDescription > "" Then Response.Write Server.HTMLEncode(objASPError.ASPDescription) & "<br>"

  blnErrorWritten = False
  
  Response.Write "<B>"
  
  If objASPError.Description > "" Then Response.Write Server.HTMLEncode(objASPError.Description) & "<br>"

  ' Only show the Source if it is available and the request is from the same machine as IIS
  …
       文件中第3行是Response.Write语句的第2行。报告错误信息时,VBScript解释器忽略一行中的引导空格和制表符。所以在数完26个字符之后,可以找到语法错误的地方,这里明显缺少了一个双引号。加上双引号后再运行这个页面,我们可以得到如图7-2所示:

       这次又是另外一个简单错误。实际上错误出现在第3行而不是第4行。我们漏掉了第三行末尾的续行符'_'。程序代码应该是:
<%
       Response.Write "The repayments for your loan are $" & chrPayment _
                            & " per " & strInterval & ", due on the " &strDay & " of each " _
                            & strInterval & "."
       %>
1.  错误出现在什么地方
需要注意的是脚本解释器仅指出所发现错误的地方,但实际上那儿并不一定是错误真正出现的地方。在上例中,前面三行的语法正确的;并产生相应的输出结果,而恰恰是第4行引起问题,因为这一行是以一种非法字符开头的,脚本解释器没有意识到这一行是上一行的一部分。
这样的错误是普遍存在的,因为通常我们主要考虑的是要输出的文本内容,而不是双引号、连字符(在VBScript中为“&”)、续行符等的正确顺序。
对于关键字、内部函数名拼写错误或函数的非法参数列表而引起的语法错误,通常比较容易发现,因为错误信息提示可能就指出了错误的实际位置。例如:下面这段代码是想把明天的日期写入页面。
Response.Write DateAdd(Now(),"d", 1)
实际得到的结果如图7-3所示:

这是因为DateAdd函数的语法应该是:
DateAdd (interval_string, interval_number, start_date)
所以应该改写为如下的代码:
Response.Write DateAdd("d", 1, Now())
脚本解释器检测到了我们为第二个参数提供的是一个字符型数据,而DateAdd函数需要的是整型数据类型。
代码结构和脚本结构
语法错误的另一个原因是:当制作网页时使用嵌套的或复杂的脚本结构,如If Then … Else … End If或者Do While … Loop。这有时会造成难以找到的语法错误。
例如下面这段程序:
<%
If Len(Request.Form("cmdSet")) Then
       strCounterName = Request.Form("lstSet")
       strNewValue = Request.Form("txtSet")
       If IsNumeric(strNewValue) Then
              intNewValue = Cint(strNewValue)
              objCounters.Set strCounterName, intNewValue
              Response.Write "Set counter " & strCounterName & " to " & strNewValue
       Else
              Response.Write strNewValue & " is not a valid number"
              If Len(Request.Form("cmdRemove")) Then
                     strCounterName = Request.Form("lstRemove")
                     objCounters.Remove strCounterName
                     Response.Write "Removed counter " & strCounterName
       End If
End If
%>
产生的错误如图7-4所示:

为什么提示在网页程序中需要一个End语句呢?看一下程序就可以发现,丢失了一个End If,而不是End,在程序的最末尾应该还有另一个End If。
                     …
                     Response.Write "Removed counter " & strCounterName
       End If
End If
       End If
       %>
       在这种情况下,根据代码的缩排格式可以很容易地找到相应的错误。特别当错误信息指出错误的大致位置时,很快就可以找到错误位置。然而,这段代码很短,如果在分界符<%…%>中另外还有40行代码,那么错误行号仍然可能指向最后一行(line 56);并且如果在新的代码中的其他脚本结构搞乱了嵌套的结构,错误可能会指向另一个位置。
2.  关于JScript
如果你不是一位JavaScript高手,并且确实想试验一些语法错误,那么就从VBScript切换到JScript。JScript比VBScript对程序编写的要求更严格,并且对关键字和变量名大小写敏感,看下面的程序段。
<%
var datToday = new Date();
Response.Write(datToday.GetMonth());
%>
运行这段程序会产生“Object doesn’t support this property or method”(对象不支持这种属性或方法)错误,如图7-5所示:

原因很简单,返回月份数的JScript函数是getMonth,而不是GetMonth。下面这段程序就可以正常运行。
<%
var datToday = new Date();
Response.Write(datToday.getMonth());
%>
当然,如果重试这段程序,可能得不到同样的错误消息。我们第一次运行这段程序时,得到如图7-6所示的错误。

第2行有什么错误?如果使用JScript解释器,没有错误出现。错误消息说明,这是一个VBScript语法错误。用VBScript解释器分析JScript程序,所以会得到奇怪的错误消息。
记住正在使用语言
这所以出现上述错误是因为在页面的代码前面忘记加@LANGUAGE指令。缺省是VBScript(如果在注册表或在Internet Services Manager中没有改变它),所以VBScript引擎用于处理前面不带@LANGUAGE指令的程序。即使一直使用专为自己的服务器设置的缺省语言,始终使用@LANGUAGE指令是避免产生上述错误的好方法。这样,如果把网页移到另一个缺省语言不同的服务器上,也会得到预期的结果。
这里讲述的内容不可能覆盖所有可能遇到的语法错误,人们往住想知道为什么会出现错误,而错误信息提示并不总是像人们希望的那样准确。理想的方式应该是ASP给我们提供一个简洁的错误显示页面,有对错误的全面精确的描述,甚至询问我们是否想自动处理错误。事实上应用程序Microsoft Script Debugger正试图为我们提供类似的功能,本章后面要对其进行讨论,也要概括避免出现语法错误的一些要点。现在,我们继续研究经常在网页中出现的第二类错误。
7.1.2 语义或“运行期”错误
       语法错误的发现和处理是令人烦恼的,但在编程中会遇到一些真正“令人兴奋”的另一类型的错误——语义错误(semantic error)或称“运行期”错误(runtime error)。这类错误仅当运行一个脚本代码或其他程序时才会发现。换句话说完整有效的代码已经通过解释器或编译器的解释或编译,在执行时产生了错误。术语“运行期错误”通过是指语义错误的结果,也就是说这类错误存在于代码的语义中,当代码运行时它们才变成可见的。
这种区别来自于这种事实:程序编译器或解释器在处理程序代码之前必须建立一种内部代码的描述,涉及多种结构开头和结尾的匹配,以便标明每种结构包含什么内容,然后分析每个句子,以便知道如何执行这个句子。例如,如果在程序代码中有一个If Then … Else … End If 结构,解释器或编译器做的第一步工作就是分析哪些语句在“Then”的部分,哪些在“Else”部分。这一步的目的是,在对结构中的If条件进行测试之后,可以决定该到哪个分支去执行。
编译器(诸如在编程语言像Visual Basic和C++中见到的那种)和解释器(诸如用于像VBScript和JScript那样的脚本语言的解释器)之间真正区别在于:编译器不试图运行程序代码,而是在对源程序进行两次预处理后,形成二进制指令或符号代码,并形成一个.exe文件或.dll文件。解释器不含有代码的文件,而是在运行时逐步执行。
1.  使运行停止的错误
如果程序中含有一个语义错误,通常在运行时可得到提示。如果幸运的话,当错误发生时,程序会停止,这样可以容易地找出错误所在。例如,下面这段程序定义了一个有六个元素的数组。
<%
Dim arrValues(5)                  'to hold six elements, indexed from 0 to 5
ArrValues(6) = "Whoops, got an error"
%>
如果试图读或设置下标为6的元素值,可以得到一个运行期错误,如图7-7所示:

注意这里的错误类型是“runtime”(相当于语义)错误,而不是语法错误。错误信息显示了错误所在行数和错误的描述,有助于我们比较容易地找到相应的错误。但这是一个简单的例子,在更复杂的程序代码中,这种错误可能出现在一些遍历一些值并把它们加到一个数组中程序中。如下所示:
<%
       Dim arrValues(5)                                       ' to hold six elements
       For intLoop = 0 To intListCount                  ' the number of items in some list
              arrValues(intLoop) = Request.Form("SelectedItems")(intListCount)
       Next
%>
这种情况下,很可能是得到了过多的列表条目,或者是数组的索引不够,根据代码的要求,可以判断是那种错误,并且能够通过增加数组大小来解决这个错误。
<%
Dim arrValues(10)                                     ' to hold eleven elements
For intLoop = 0 To intListCount                  ' the number of items int some list
arrValues(intLoop) = Request.Form("SelectedItems")(intListCount)
       Next
       %>
       或者相应地设置循环的参数来解决处理这个错误。
<%
Dim arrValues(5)                                       ' to hold six elements
IntArrayMax = intListCount
If intArrayMax > 5 Then intArrayMax = 5
For intLoop = 0 To intArrayMax                 ' only add the first six items
arrValues(intLoop) = Request.Form("SelectedItems")(intListCount)
       Next
       %>
       许多其他运行期错误能够使网页运行停止,诸如一些组件或对象的实例化失败,原因是有ProgID错误,或者是因为组件没有正确安装。在这些情况下,结果总是给出“ActiveX Cannot Create Object”错误提示信息,后面跟着调用Server.CreateObject方法的行号。
2.  产生错误结果的错误
上面提到,如果遇到一个使程序代码停止的运行期错误,我们可能是幸运的。但是另一种情况是程序能很好地执行,好像什么也没有发生,最后产生一个错误的结果。这是最难发现和解决的错误,因为意识不到哪里出错了。例如,假设有一个网页,这个网页把用户的生日作为日期型的值,并且单独显示日期元素(可以把它们作为三个条目加到一个数据库中)。
<%
' get the value from the Request and display it
datBirthdate = Request.Form("Birthdate")
Response.Write "The value you entered is: " & datBirthdate & "<P>"

' get the individual date elements
intDay = Day(datBirthdate)
intMonth = Month(datBirthdate)
intYear = Year(datBirthdate)

' and display them
Response.Write "Day: " & Cstr(intDay) & "<BR>"
Response.Write "Month: " & Cstr(intMonth) & "<BR>"
Response.Write "Year: " & Cstr(intYear) & "<BR>"
%>
图7-8是结果,是用美国日期风格月/日/年显示的,好像一切都没有问题。

然而如果输入一个非法日期,或者让输入文本框空着,便得到一个运行期错误,如图7-9所示:

(1)    如果不是一位JScript专家
在寻找错误时,这不是一个大问题,因为我们能够迅速发现为什么会出现错误。事实上网页停止运行有助于我们跟踪错误。然而意外的错误可能会发生。例如,用JScript重写程序代码,由于不是一位JScript专家,里面出现一些细小错误。
<%
// get the value from the Request and display it
var datBirthdate = new Date(Request.Form("Birthdate"));
Response.Write("The value you entered is: " + datBirthdate + "<P>");

// get the individual date elements
intDay = datBirthdate.getDay();
intMonth = datBirthdate.getMonth();
intYear = datBirthdate.getYear();

// and display them
Response.Write("Day: " + intDay.toString() + "<BR>");
Response.Write("Month: " + intMonth.toString() + "<BR>");
Response.Write("Year: " + intYear.toString() + "<BR>");
%>
图7-10即是运行结果,尽管程序没有停止运行并给出运行期错误,还是马上看出其中有些问题,月份不可能是0。

问题出现的原因在于JScript的getMonth函数返回的结果为0~11范围内的数,因此需要再加1,才能得到正确的结果。
intMonth = datBirthdate.getMonth() + 1;
(2)    衍生错误
即使不把初始值赋给网页去和结果比较,上面这种错误也可能是相当明显的。然而,如果面对的是一个数据库系统,并且没有看到显示出不正确的结果,可能不知道为什么程序不能正确地更新数据库。更糟糕的是,如果简单地把数值做为整型数据存入数据库,可能直到有人试图对这个数据查询时才能发现这个错误。
现在,发现大约有十二分之一的成员出生在0月份可能会使人吃惊,并会引起一些问题。记住,不仅仅是那些1月份出生的人员存在数据库中的信息不正确,而且每个成员都是这样。如果有许多应用程序都能增加和修改这个数据库中的记录,跟踪这个错误可能是艰苦的工作,特别是,不能去查找错误出现在哪个程序行,而是首先要找出错误出现在哪个应用程序中。
(3)    掌握日期的用法
在上面的程序中出现的日期型数据的错误不是非常明显,不论使用都输入什么样的日期,程序代码只能给出0~6之中的值,原因在于编码中的设定,特别是从VBScript转换到JScript时。在JScript中,getDay函数返回的周中的某一天,而不是月中的某一天,这等价于VBScript中的Weekday函数,getDay函数的返回值是0(代表星期日)到6(代表星期六)。
注意VBScript的Weekday函数返回1(代表星期日)到7(代表星期六)。
因此,在JScript中由getDate函数获得某月的日期的正确代码是:

// get the individual date elements
intDay = datBirthdate.getDate();
intMonth = datBirthdate.getMonth() + 1;
intYear = datBirthdate.getYear();

运行这段程序便可得到想要的结果,如图7-11所示:

 
 
  
评论】【加入收藏夹】【 】【打印】【关闭
※ 相关链接
 ·程序调试—发现及处理错误  (2005-04-02)
 ·处理错误  (2005-04-02)
 ·防止错误  (2005-04-02)
 ·各种运行期错误  (2005-04-02)
 ·管理错误  (2005-04-02)
 ·使用Server.scripttimeout来减少  (2005-04-02)
 ·Web网站的错误控制  (2005-04-02)
 ·asp程序错误详细说明例表  (2005-04-02)
 ·ASP.NET中的错误处理支持  (2005-04-02)
 ·如何正确理解PHP的错误信息  (2005-04-02)

   栏目导行
  PHP编程
  ASP编程
  ASP.NET编程
  JAVA编程
   站点最新
·致合作伙伴的欢迎信
·媒体报道
·帝国软件合作伙伴计划协议
·DiscuzX2.5会员整合通行证发布
·帝国CMS 7.0版本功能建议收集
·帝国网站管理系统2012年授权购买说
·PHPWind8.7会员整合通行证发布
·[官方插件]帝国CMS-访问统计插件
·[官方插件]帝国CMS-sitemap插件
·[官方插件]帝国CMS内容页评论AJAX分
   类别最新
·在ASP中使用数据库
·使用ASP脚本技术
·通过启动脚本来感受ASP的力量
·学习使用ASP对象和组件
·解析asp的脚本语言
·初看ASP-针对初学者
·ASP开发10条经验总结
·ASP之对象总结
·ASP与数据库应用(给初学者)
·关于学习ASP和编程的28个观点
 
关于帝国 | 广告服务 | 联系我们 | 程序开发 | 网站地图 | 留言板 帝国网站管理系统