随笔-313  评论-12138  文章-1  trackbacks-256

使用ASP.NET AJAX异步调用Web Service和页面中的类方法(2):处理异步调用中的异常

本文来自《ASP.NET AJAX程序设计 第II卷:客户端Microsoft AJAX Library相关》的第三章《异步调用Web Service和页面中的类方法》,请同时参考本章的其他文章

3.3 处理异步调用中的异常

在传统的Web应用程序中,处理异常相对来说比较简单——即使开发者不作任何处理,浏览器也会默认地将收到的异常信息显示在浏览器中。而对于Ajax应用程序来说,事情却并不那么简单。Ajax程序“异步”的天性加上其后台运行的行为,让用户乃至开发者都很难判断某次对服务器的调用是否顺利完成,浏览器自然也对Ajax程序运行时发生的异常无能为力。

在本章前面两节中,借助于ASP.NET AJAX异步通讯层的帮助,我们已经能够容易地从客户端向服务器端发起异步HTTP请求——在理想情况下,这自然不会有什么问题,也足够使用。然而,Web程序在运行中会有很多不确定性,从网络状况的不稳定到开发者的粗心大意,任何一个难以预料的问题均会导致某次异步调用以失败告终。

因此,在ASP.NET AJAX异步通讯层的实现中,自然也内建了对异步调用时异常的处理方法。还记得前面曾经介绍过的在客户端调用Web Service代理的语法吗?

[NameSpace].[ClassName].[MethodName](param1, param2 …, callbackFunction)

在调用成功的回调函数callbackFunction的后面,我们还可以提供另一个调用失败的回调函数。这样,客户端调用Web Service代理的语法就变为:

[NameSpace].[ClassName].[MethodName](param1, param2 …, onSucceeded, onFailed)

注意其中粗体部分新添加的onFailed回调函数,该函数将在本次异步通讯出现异常时由ASP.NET AJAX异步通讯层调用。而onSucceeded的行为则不会收到任何影响,仍将在成功调用后执行。

onFailed回调函数将接受一个类型为Sys.Net.WebServiceError的参数,表示异常对象。其函数签名将类似如下所示:

function onFailed(error) {
    // 取得异常信息并处理
}

ASP.NET AJAX的客户端Sys.Net.WebServiceError类型封装了异步请求服务器时可能发生异常,它提供了若干个只读的属性,提供了对异常信息的详细描述。Sys.Net.WebServiceError类型的属性如表3-1所示。

表3-1 Sys.Net.WebServiceError类型的属性

  1. 属性名:描述
  2. exceptionType:获取服务器端异常的具体类型
  3. message:获取详细的异常描述信息
  4. statusCode:获取造成异常的HTTP响应的状态码
  5. stackTrace:获取服务器端异常的堆栈跟踪信息
  6. timedOut:获取一个布尔值,表示异常是否是由于网络连接超时造成的

注意:根据ASP.NET AJAX客户端组件的命名规范,访问属性均需要在属性名称前加上“get_”或“set_”前缀。例如,若想得到某个Sys.Net.WebServiceError类型异常的message属性值,则应该按照如下方式书写代码:

var errorMessage = errorObj.get_message();

下面让我们用一个简单的示例程序演示在客户端调用Web Service代理时发生异常的处理办法,以及Sys.Net.WebServiceError类型中各个属性的使用方法。

该示例程序是一个除法的计算器,程序将借助ASP.NET AJAX异步通讯层将用户输入的除数和被除数发送至服务器,服务器完成具体的触发计算过程后再将结果返回至客户端显示出来。程序运行的初始界面如图3-4所示。

图3-4 除法计算器的初始界面

输入除数和被除数,然后点击问号(“?”)按钮,程序将调用服务器端Web Service完成本次除法,并将商显示在问号按钮中,如图3-5所示。

图3-5 执行一次普通的除法

若是用户输入的除数为0,那么显然服务器端执行时将抛出异常。我们不会在服务器端对该异常进行处理,因此将导致本次异步调用失败,客户端也会显示出异常的详细信息。如图3-6所示。

图3-6除数为0导致本次除法失败

让我们先从服务器端的Web Service入手。我们将该Web Service命名为MathService,并在其中定义了一个名为Divide()的方法,用来执行除法操作。Divide()方法所接受的两个参数分别代表被除数和除数,其逻辑非常简单,代码如下:

[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[ScriptService]
public class MathService : System.Web.Services.WebService
{
    [WebMethod]
    public int Divide(int a, int b)
    {
        return (int)(a / b);
    }
}

这里有必要再次提醒一下,Web Service类要添加[ScriptService]属性,其中需要暴露给客户端的方法也要添加[WebMethod]属性——这些都是允许从客户端调用该Web Service代理的必要条件。

在ASP.NET页面中,添加ScriptManager控件以及上述Web Service的引用:

<asp:ScriptManager ID="sm" runat="server">
    <Services>
        <asp:ServiceReference Path="Services/MathService.asmx" />
    </Services>
</asp:ScriptManager>

然后在ASP.NET页面中定义程序的界面:

<input id="tbA" type="text" style="width: 40px" /> / 
<input id="tbB" type="text" style="width: 40px" /> = 
<input id="btnInvoke" type="button" value="?" 
    onclick="return btnInvoke_onclick()" />
<div id="result"></div>

其中前两个<input />(id分别为tbA和tbB)用来让用户输入被除数和除数;第三个<input />(id为btnInvoke)则作为按钮(type="button")用来触发对服务器端Web Service的调用,并显示除法完成后的商;下面id为result的<div />用来显示可能出现的异常信息。

function btnInvoke_onclick() {
    var a = $get("tbA").value;
    var b = $get("tbB").value;
    MathService.Divide(a, b, onSucceeded, onFailed);
}

注意其中粗体部分,即调用Web Service客户端代理的一行。其中不但传入了被除数和除数(a和b),还传入了成功调用后的回调函数onSucceeded以及失败时的回调函数onFailed。

成功调用时的回调函数onSucceeded()比较简单,这里不赘:

function onSucceeded(result) {
    $get("btnInvoke").value = result;
    
    $get("result").innerHTML = "";
}

失败时的回调函数onFailed()才是本示例程序的重点:

function onFailed(error) {
    
    // 取得异常信息
    var stackTrace = error.get_stackTrace();
    var message = error.get_message();
    var statusCode = error.get_statusCode();
    var exceptionType = error.get_exceptionType();
    var timeout = error.get_timedOut();
   
    // 显示异常信息
    $get("result").innerHTML = 
        "<strong>Stack Trace: </strong>" +  stackTrace + "<br/>" +
        "<strong>Service Error: </strong>" + message + "<br/>" +
        "<strong>Status Code: </strong>" + statusCode + "<br/>" +
        "<strong>Exception Type: </strong>" + exceptionType + "<br/>" +
        "<strong>Is Timeout: </strong>" + timeout;
        
    $get("btnInvoke").value = "?";
}

可以看到,onFailed()函数首先取得了传递进来的Sys.Net.WebServiceError对象的各个属性,然后再依次显示到id为result的<div />中。

这样就完成了本示例程序的编写。运行该程序并尝试做一些除法,若程序编写正确的话,你将看到如图3-4、图3-5和图3-6所示的界面。

当然,本示例程序的目的是为了演示调用服务器端Web Service时发生异常的处理方法,所以自然事无巨悉地将所有异常信息均显示了出来。而在实际开发中,我们则不应该完整显示出此类唐突的异常细节。通常的做法是根据不同的异常进行相应的处理,并在需要的情况下再为用户显示出相对友好的提示消息。

posted on 2007-06-06 00:34 Dflying Chen 阅读(6320) 评论(27) 编辑 收藏

评论:
#1楼 2007-06-06 00:44 | 代码乱了      
支持!
老大,还不睡啊?

 回复 引用 查看   
#3楼[楼主] 2007-06-06 08:46 | Dflying Chen      
@代码乱了
:)

 回复 引用 查看   
#4楼[楼主] 2007-06-06 08:47 | Dflying Chen      
@http://www.27ct.cn
什么?

 回复 引用 查看   
#5楼 2007-06-06 08:52 | Anthan      
不错,昨天刚看到第一卷中的异常处理,没想到今天就能看到第二卷了。
有个问题,获得异常以后可以在客户端将异常信息写入到日志文件或者数据库中吗?像本文中这样写的话是不是记到客户端的日志文件中去了?
但是如果在WebService中做异常处理的话那异常还能抛回到客户端由onFailed来捕获吗?

 回复 引用 查看   
#6楼[楼主] 2007-06-06 08:55 | Dflying Chen      
@Anthan
日志文件一般都是写到服务器端的,只要在服务器端Catch之后重新Throw出来就可以同样在客户端捕获了。
不过若想把日志写入到客户端,那么既没什么意义,JavaScript也不允许这样做。

 回复 引用 查看   
#7楼 2007-06-06 11:11 | Anthan      
@Dflying Chen
恩,异常是要在服务器端去catch以备后查,客户端显示友好界面应该就好了。

 回复 引用 查看   
#8楼[楼主] 2007-06-06 11:16 | Dflying Chen      
@Anthan
这些都是没问题的

 回复 引用 查看   
#9楼 2007-06-06 12:11 | wqx[未注册用户]
Dflying Chen
为什么在使用AJAX控件GetSlides时,只要路径有点点改变或使用该方法的文件和装照片的文件夹不在同一目录下,有时他们不在根目录下都是有:“The server method'GEtSlide'failed with the following error:
System.Securty.SecurityException---请求“System.Security.Permissions.FileOPermissiom.mscorlib,Vrsion=2.0.0.0,Culture=neatral,PublicKeyToken=b77a5c561934e089"类型的权限已失败。”这个警告弹出来的,而在就算做为一个页面来显示是正常的,把它做成一个用户控件来使用,不论路径在哪里,都是有“GetSlides”failed.....的警告出来

 回复 引用   
#10楼 2007-06-06 12:26 | 小鬼[未注册用户]
学习中...
 回复 引用   
#11楼[楼主] 2007-06-06 13:10 | Dflying Chen      
@wqx
看似好像是安全性问题……

 回复 引用 查看   
#12楼[楼主] 2007-06-06 13:10 | Dflying Chen      
@小鬼
:)

 回复 引用 查看   
#13楼 2007-06-06 14:16 | Leepy      
[NameSpace].[ClassName].[MethodName](param1, param2 …, onSucceeded, onFailed),后面是否还有个onTimeOut?在什么时候会发生onTimeOut?
 回复 引用 查看   
#14楼 2007-06-06 16:29 | zjw2004112
受益非潜,辛苦了,学习..........
 回复 引用   
#15楼 2007-06-06 16:30 | zjw2004112
第三卷大概讲那方面的内容,期待中...........
 回复 引用   
#16楼 2007-06-06 16:57 | Leepy      
@zjw2004112
第三卷的内容

《ASP.NET AJAX程序设计——第III卷:高级内容》

本卷将介绍ASP.NET AJAX相关的高级主题,包括ASP.NET AJAX应用程序的调试方法和技巧、性能优化、部署、源代码结构及部分源代码分析、客户端/服务器端自定义组件/控件的开发等内容。

除此之外,这一卷还将更加紧扣Ajax的核心思想——提高用户体验,包括用户心理学、用户行为分析理论等内容,深层次挖掘提高用户体验的方法。


 回复 引用 查看   
#17楼[楼主] 2007-06-06 17:41 | Dflying Chen      
@Leepy
超时的时候也会调用onFailed的,稍候章节中就有介绍超时的出现原因,这两天就会放出。

 回复 引用 查看   
#18楼[楼主] 2007-06-06 17:41 | Dflying Chen      
@zjw2004112
谢谢支持!

 回复 引用 查看   
#19楼[楼主] 2007-06-06 17:42 | Dflying Chen      
@zjw2004112
第三卷是高级内容,讲的东西比较杂:
本卷将介绍ASP.NET AJAX相关的高级话题,包括ASP.NET AJAX应用程序的调试方法和技巧、性能优化、安全性问题、部署、源代码结构及部分源代码分析、客户端/服务器端自定义组件/控件的开发等内容。
除此之外,本卷还将更加紧扣Ajax的核心思想——提高用户体验,包括用户心理学、用户行为分析理论等内容,深层次挖掘提高用户体验的方法。

 回复 引用 查看   
#20楼[楼主] 2007-06-06 17:42 | Dflying Chen      
@Leepy
谢谢!

 回复 引用 查看   
#21楼 2007-06-07 23:07 | 乐趣电子书[未注册用户]
@代码乱了
 回复 引用   
#22楼 2007-07-10 17:11 | winnerzone      
写的非常好!谢谢.
 回复 引用 查看   
#23楼[楼主] 2007-07-10 19:01 | Dflying Chen      
@乐趣电子书

 回复 引用 查看   
#24楼[楼主] 2007-07-10 19:01 | Dflying Chen      
@winnerzone
谢谢支持

 回复 引用 查看   
#25楼 2007-09-21 21:27 | Joe Xia[未注册用户]
对象不支持属性或方法,var stackTrace = error.get_stactTrace();
 回复 引用   
#26楼 2007-10-18 10:54 | yangyi2336[未注册用户]
谢谢你的好文章!!
 回复 引用   
除非特别声明,本站内所有资源,包括但不限于文章,代码,图片等,均应用于Dflying版权说明
关于ASP.NET AJAX,您可以:
直接阅读ASP.NET AJAX文章分类
Atlas文章打包下载(截至4/28/2006)
加入ASP.NET AJAX学习团队
询问关于ASP.NET AJAX的问题
加入ASP.NET AJAX讨论群
阅读愚作《ASP.NET AJAX程序设计》
点击阅读
点击阅读


关于Windows Vista,您可以:
加入Windows Vista开发团队!
昵称:Dflying Chen
园龄:5年10个月
粉丝:127
关注:0

搜索

 
 

最新随笔

随笔分类(352)

随笔档案(313)

Blog Roll

Dflying的其他Blog

Online Chat

统计信息

积分与排名

  • 积分 - 2442908
  • 排名 - 7

最新评论

阅读排行榜

评论排行榜