admin 管理员组

文章数量: 1086019


2024年3月13日发(作者:switch是什么取向)

维普资讯

} E* _ t 自^ st… 目 t } … * ■自 i ■ 

实用第一 智慧密集 

;… … …… … … 一, … … … 

使 用ASP: 

嚣 l 

0 

AjAX取消服 务器 ≥ _馕  务 

| | 

|I 

.. 

摘要详细介绍了在客户端监视正在执行任务的服务器端框架的构建过程,使用此框架 

(以后称为进度监视器框架或PMF),可以为Web用户提供关于服务器上运行的 

操作的进度信息,此类信息通常需要大量的自定义代码才可获得。 

关键词PMF,Web,AJAX,框架,进度条 

触发远程任务 

远程任务是在服务器上执行的用于响应客户端事件的一段 

var obj=Sys.WebForms。PageRequestManager. 

getlnstance 

-f(obj.getjsl nAsyncPostBack()) 

obj.abortPostBack(): 

} 

(/script> 

代码。ASP.NET AJAX‘客户端页面触发远程任务的方法有以下 

三种:使得回发由UpdatePanel控件管理,在通过本地Web服 

务公开的应用程序后端直接调用一种方法,使用页面方法。很 

快会有第四种方法:一种Windows Communication Foundation 

(WCF)服务。 

<body> 

<fOrm id= fOrmr runat= server > 

<asp:ScriptManager ID= ScriptManagerl runat± serv. 

/> 

旦触发了服务器上的某项任务,客户端将不再控制该任 

务。仅当由任务生成的响应已下载到客户端并经过解析后,客 

户端页面才能够重新控制操作。使用PMF,可以动态地读取任 

务状态,但不存在将数据动态传送给服务器任务的机制。 

(asp:UpdatePanel ID=jUpdatePanel1 runat=’s色 戳’ 

UpdateMode="Conditiona\ > 

<ContentTemplate> 

<asp:Button runat= server ID= Button1'Text= 

二、取消任务的简便方法 

使用ASP.NET AJAX取消远程服务非常简单,但是存在 

以下两个限制。首先,该任务必须已通过UpdatePanel启动。 

其次,服务器上不需要任何额外工作来补偿任务的突然中断。 

下面是基于UpdatePanel的页面源代码: 

<htmI xmIns= http://www.w3.org/1 999/xhtml > 

<head runat="server > 

<titIe》Canceling taSks</title> 

StartTask。.。 

onclick=j Button1 lickj >l 

<hr/> 

<asp:LabeI runat= server ID= Label1 /><br/> 

</Conten盯台mplate> 

</asp:UpdatePaneI> 

<hr/> 

<asp:UpdateProgress runat= server ID= 

"UpdateProgress1 > 

(ProgressTemplate> 

<styIetype= text/css > 

撤』pdateProgress 1{ 

width:270px;background—color:#ffffgg;height:1 20px; 

<div ID= ProgressTemplatel ><P style; margin: 

5px; > 

top:40%:left;35%:position:absolute; 

border:solid 1 Dx black; 

<img alt= src= Images/indicator.gif 

align= left /> &nbsp: 

<span id= Msg >Your request has been submitted and 

it may take a while to complete. 

} 

#ProgressTemplatel{ 

fOnt—size:9pt;color:navy;font—family:verdana: 

<br/><br/>Please wait…

(Palign= center > 

</span>。 

} 

</stVle> 

<input type= button vaKte:"Cancel 

</head> 

onclick="abortTaskf) /></p> 

</div></p> 

</ProgressTemplate> 

<scriPt language= javascript type= text/iavascript > 

function abortTask f j{ 

维普资讯

…… 

FORUl_…… …一-…… ……… *-- -一一…一 …… 一…n………… ……………… …一……....…… …………… 

</asp:UpdatePrOgress> 

</form></body></html> 

在该页面中,会弹出带有“取消”按钮的进度模板,如图 

1所示。单击该按钮可以取消操作。 

图1带有取消按钮的进度模板 

从上面代码中的abortTask函数可以看出,进度模板包含 

个绑定到JavaScript代码的客户端按钮。此函数的首要任务 

是检索页面请求管理器。进行页面初始化时,页面请求管理器 

会为窗体的提交事件注册一个处理程序。这样,每次响应页面 

时,都会调用请求管理器。此时,请求管理器会生成请求主体 

的副本,并通过当前的HTTP执行器(默认指的是常见的 

X ̄MLHttpRequest对象)运行该副本。 

页面请求管理器设置部分呈现的事件模型,并跟踪正在执 

行的操作。如果存在任何挂起的操作,则Boolean属性jsI— 

nAsyncPostBack将返回true。 

当用户单击图1中所示的“取消”按钮时,页面请求管理 

器将通过其abortPostBack方法中止当前请求。页面请求管理器 

是一个独立对象,即所有调用都只能传递给一个实例。此情形 

的原因与部分呈现机制紧密相关。部分呈现由发送页面请求组 

成,包括在服务器上的整个常规处理过程(呈现阶段除外)。 

此外,这意味着视图状态将被发送,并用于重新创建服务器控 

件的上次已知正常状态。返回信息和状态更改事件是定期触发 

的,视图状态即根据这些操作进行更新。然后,更新的视图状 

态会与进行了部分修改的标记一起发送回来。 

由于视图状态的关系,需要对来自同一页面的两个异步回 

发调用进行序列化,并且每次只允许运行一个调用。由于这一 

原因,页面请求管理器上的abortPostBack方法不必指出要停止 

哪一请求,因为至多有一个挂起的请求。 

三、深入了解ab0rtP0stBack方法 

让我们进一步了解PageRequestManager类中的abortPost— 

Back方法,示例代码如下。 

fun oqrion Sys¥WebForms¥PageRequestManager¥abortPost- 

Back《) 

{ 

if《!this.jorocessingRequest&&this._request) 

{ 

廿fis.

..

requesL get_executor《).abort《): 

this.

_

request=null; 

。 

、 

如果存在挂起的请求,则管理器将指示中止请求的执行 

器。执行器是从sys,Net. WebRequestExecutor继承的一个 

JavaScript类,负责发送请求和接收响应。在Microsoft AJAx客 

户端库中,只有一个执行器类(Sys.Net.xMLHttpExecutor 

类),它使用XMLHttpRequest对象执行请求。当上述代码调 

用中止方法时,主要是告知xMLHttpRequest对象。从另一个 

角度来讲,它仅指示执行器用来接收响应数据的套接字必须关 

闭。 

现在,假设远程任务在服务器上执行破坏性操作。例如, 

假设为用户提供了~次机会,使其能够通过单击一个按钮来删 

除数据库表中的少量记录。通过上述过程尝试取消操作实际上 

不会停止服务器操作。它所能实现的所有功能就是关闭用来接 

收确认消息的套接字。PageRequestManager对象上的abortPost- 

Back方法仅仅是一个客户端方法,对服务器中运行的操作不 

会起到任何作用。 

四、设计不问断任务 

要使中止请求对服务器操作有效,任务必须是不问断的。 

换句话说,任务必须定期检查是否存在来自客户端的指示任务 

退出的说明。 

当首次实现PMF时,框架的客户端和服务器元素共享一 

个通用数据容器,服务器使用该容器写入关于其进度的数据, 

客户端使用该容器读取此数据,以更新用户界面。要使得服务 

器代码接收并处理动态客户端反馈(如单击“取消”按钮),需 

要用到一些增强功能。目前,进程服务器API基于以下约定: 

public interface IProgressMonitor 

{ 

void S;etStatus《int tasklD,object message): 

strin!igGetStatus《inttasklD) 

bool ShouldTermihate《inttasklD): 

vc ̄idRequestTermination《int tasklD): 

) 

这里添加了两个新方法:ShouldTerminate和RequestTermi— 

nation。前者返回…个Boolean值,表明是否应终止正在执行的 

任务。RequestTern1ination方法为希望结束任务的客户端指示 

API中的入口点。调用此方法时,它会在数据容器(ASP.NET 

维普资讯

………………………… … … ………………………一 

实用第一 智慧密集 

缓存)中创建一个与任务相关的入口,ShouldTerminate会检查 

此入口以确定是否请求了中断。 

上文中定义的IProgressMonitor接口指示服务器上某个应用 

程序的预期行为。可以在可能使用不同数据容器的各种类中实 

现该接口。笔者使用名为InMemoryProgl essMonitor的ASP.NET 

可监视的不问断任务的典型结构: 

public staticstring ExecuteTask(inttasklD) 

{ 

InMemoryProgressMonitor progMonitor=new fnMemo- 

ryProgressMonitor(): 

缓存创建了一个示例类,核心代码如下: 

public clasS InMemoryProgressMonitor:IProgressMonitor 

{ 

pub№const int MAX

_

TIME

_

MINUTES:5 

//从任务调用此方法 它将任务的当前状态写入内部数据 

//存储 该状态以对象的形式表示. 

publicvoid SetStatus(int tasklD,object message) 

HttpContext.Current ̄Cache Insert( 

taskID ToString() message,nulI, 

DateTime.Now AddMinutes(MAX_TIME INUTES), 

Cache.NoSlidingExpiration): 

) 

//从内部数据存储读取指定任务的当前状态,并将其以 

/ 字符串的形式返回至0客户端 

publicstringGetStatuS《inttasklD) 

{ 

object o#HttpContext.Current.Cache【task

lD.ToString()】: 

returnO==null 7string.Empty:(string)o: 

) . 

//如果客户端发出了终止指定任务的请求,则返回true. 

public bool ShouldTerminate(int tasklD) 

{ 

string taskResponsefD=GetSlotForResponse(taskiD): 

return HttpContext.Current.Cache[taskResponselD】!= 

n ull 

) 

//在内部数据存储中创建与任务相关的入口,以指示 

//客户端发出了终止请求. . 

publicVOid RequestTermination(inttasklD) 

{ 

string taskResponselD:GetSlotForResponse(tasklD): 

HttpContext Current.Cache.Insert( 

taskResponse lD (object)false,nu 

DateTime.Now.AddMinutes(MAX_TIME_MINUTES), 

Cache.NoSlidingExpiration): 

) 

private string GetSIotForResponse(int tasklD) 

{ 

returnString.Format( {0)一Response ,tasklD): 

) 

) 

要支持动态中断,相同的任务将定期调用ShouldTermi— 

nate,以便在客户端请求退出时获得通知。下面的代码显示了 

if《progMonitor.ShouldTerminate(tasklD)) 

return Task aborted一一0%done : 

??窝一 

progMonitor.SetStatus(tasklD, 0 ): 

DoStep(1) 

if(progMonitor Sh0uldTerminate(tasklD)) 

return Task aborted一一5%done : 

//第二步 

progMonitor.SetStatus(tasklD, 5 l: 

DoStep(2) 

if(progMonitor.ShouldTerminate(tasklD)) 

return Taskaborted一一45%done : 

//第三步 

progMonitor SetStatus(tasklD, 45 ); 

DoStep(3): 

if(progMonitor.ShouldTerminate(tasklD)) 

return Taskaborted一一69%done : 

,,最黾一步 

progMonitor.SetStatus(tasklD, 69 ): 

DoStep(4): 

if(progMonitor.ShouldTerminate(tasklD)) 

return Taskaborted一一1O0%done : 

return Task completed at: +DateTime Now.ToString(): 

) 

上面代码中显示的方法用于协调组成远程任务的各个步 

骤。该任务可以是应用程序的中间层的一部分,也可以作为工 

作流实现。它在各步骤问必须是相互关联的,以便客户端插入 

到其中读取状态和请求终止。 

五、客户端代码 

可以使用页面或Web服务方法(如ExecuteTask方法)启 

动任务,或在UpdatePanel区域中运行用于触发远程任务的 

JavaScript服务器代码。 

<asp:UpdatePanel runat= server ID= UpdatePanel1 > 

<ContentTemplate> 

<asp:Bu ̄on runat= server ID= Button1 Text= Start 

Task、.. 

OnClick= Button1 Click /> 

<hr/> 

<asp:Label runat= server ID= Label1 /><br/> 

</ContentTemplate> 

</asp:UpdatePanel> 

在Button1 Click事件处理程序中,定义了远程任务,并使 

其调用进度监视器对象以及SetStatus和ShouldTerminate方法。 

维普资讯

…… 

阳删啊……………………………………………………………………………… .…………………………… 

要突然终止一个远程任务,需要在进度模板中添加一个“取 

消”按钮,它可以是UpdateProgress控件,也可以是用户定义 

的一个<div>块。此时, “取消”按钮的单击处理程序不指 

向页面请求管理器中的abortPostBack方法,而是指向客户端进 

度API中自己的中止方法。 

<script type= text/javascript > . 

var progressManager=nuII: 

vartasklD=null; 

function pageLoad《) 

{ 

progressManager=neW Samples.PMF2.Progress《) 

} 

function abortTask《) 

{ 

progressManager.abortTask(taskl D) 

} 

</script> 

下面让我们来看一下经过修改的客户端进度API。此AP] 

在progress.jS文件中进行编码,因此必须链接到计划使用不问 

断或可监视任务的每个ASP.NET AJAX页面。 

<asp:ScriptManager ID= ScriptManagerl runat= server 

EnablePageMethods:"true > 

<Scripts> 

<as ScriptReference path= random。js /> 

<asp"ScriptReference path: progtess.js ,> 

</Scripts> 

</asD:ScriptManager> 

random.js文件与progress js相关,定义了一种可生成随 

机数量任务的方法。要从客户端跟踪远程任务的状态,需要定 

期轮询服务器。要停止正在执行的任务,或者更确切地说,要 

发出一个请求以停止任务,需要调用一个服务器方法,该方法 

是由进度监视器服务器API作为应用程序后端的一部分发布 

的。 

//取消操作 

function Samples¥PMF2¥Progress¥abortTask《){ 

PageMethods TerminateTask(taskiD,null,null,nul1); 

} 

笔者选择使用页面方法发布此客户端可调用函数。整个解 

决方案的架构如图2所示。 

用户单击“取消”按钮时,会触发一个带外调用以执行 

TerminateTask方法,此方法是作为页面的后续代码类上的页面 

方法定义的。TerminateTask方法在内部数据存储(ASP.NET 

缓存)中创建一个与任务相关的入口。此入口是按带有 

“Quit”后缀的任务ID命名的。设计为不间断的任务在执行 

过程中的各个阶段检查此入口。如果找到了该入口,则服务器 

任务中止,如图3所示。 

臻 : 

 ̄tfi-筹岔辑鹄 

醛执意 访捌一 

十弛 擗 

圆 

图2双向进度监视器框架 

……一 * 《 —B 

*女 …÷ 慵 斡 4 。 ‘ 皇妻鲤羹鏖嚣嚣襄盛 矍蠹 一.;塞:.彗; 鍪整妻 冀瑟蕊 

g 戛 擎。 

一 ~ 。 

蒜…爱曼 

■■■■■■●■II r 

# : 

一 |; 

图3用户单击“取消”按钮。结束服务器任务 

通过此方式实现的任务取消将更有效。如果在UpdatePan— 

el刷新过程中仅中止客户端回发,所导致的全部结果将是关闭 

用于接收响应的客户端套接字。对服务器上运行的代码不会产 

生任何影响,也不存在以编程方式停止对Web服务或页面方 

法的远程调用的内置方法。在这种情形下,JavaScript代理类 

完全隐藏了正被用于推送调用的请求对象。虽然请求对象及其 

执行器具有中止方法,但在服务方法调用的上下文中找不到对 

它的引用。 

最后,如果需要允许远程任务控制,进度指示器模式是唯 

可行的方法。设置并行信道来监视状态,并向正在运行的任 

务传递更多信息(如退出命令)。这种相同的体系结构允许客 

户端动态更改参数或请求其他操作。双向进度监视器框架是双 

工信道,服务器任务及其JavaScript客户端可使用该信道交换 

消息形式的数据。 

六、事务 

至此,已创建了一个框架用以监视和停止ASP NET AJAX 

任务。关键需要注意的是,该框架只是通知任务用户请求其终 

止。如果设计正确,任务会立刻停止并返回。但对于已完成的 

工作会如何处理呢? 

般情况下,当任务突然中断时,应撤消它所做的所有更 

改并返回。但进度监视器框架无法实现此功能。不过,如果将 

远程任务封装在事务中,即可在该任务中断后立即回滚。另一 

壤 

维普资讯

…………………………………………… …………… 

实用第一 智慧密集 

. 。 。 … 

种选择是使用工作流。在这种情形下,将任务封装在Transac— 

tionScope活动中,使用Code活动设置当前状态并检查是否有 

builder.append( width:~): 

builder.append(perc+ % > ): 

buiIder.append(  </td><td bgcolor= 

builder.append(this._todoBackColor): 

builder.append( width:一): 

builder.append(1O0一perc+ % > ): 

builder.append( </td></tr></table> ): 

终止请求。如果任务必须终止,会引发异常并自动导致事务回 

滚。并非所有操作都可轻松地自动回滚,一般情况下,可以实 

现TransactionScope块内部的任务,并安全有效地使用用于实 

现Transaction界面的所有对象。如果这样做,则所有对象都将 

相应地回滚或提交。其中每个对象都了解如何撤消其更改。 

底线是从客户端监视远程任务的进度,此操作相对简单, 

不会产生严重的负面影响。PMF在其上增加了一些好的抽象, 

并提供了一些现成的编程工具。使任务不问断会引发一些其他 

问题,当任务具有固有的事务语义时尤其如此。编写代码来只 

通知任务用户请求其终止是游戏中相当简单的一部分。真正复 

杂的部分在任务实现及其补偿策略中。 

七、生成进度条 

在本文即将结束时,介绍一下如何使用JavaScript轻松生 

成进度条标记,并使其更易于维护。进度条可以通过构建 

HTML表生成,代码如下: 

<tablewidth= 100% > 

<tr> 

<td>69%done</td> 

</tr> 

<tr> 

<tdbgcolor: blue width= 69% > </td> 

<tdWidth= 31% ></td> 

</tr> 

</table> 

此表包含两行:附带文本和仪表。仪表使用两单元格的行 

来呈现,其中的单元格已给定背景色和成比例的宽度。 

仔细查看上述标记,至少能够识别三个参数:面向用户的 

消息、要显示的值,以及要对“已完成”和“未完成”区域使 

用的颜色。这样就不再生成字符串形式的标记,创建 

JavaScript类会更简洁。Samples.GaugeBar类的实现实现方法如 

下: 

function Samples¥GaugeBar¥generateMarkup(text,perc) 

vatbuilder=newSys.StringBuilder(~): 

builder.append( <table width: 100% ><tr><td 

cols—pan= 2 >&nbs ): 

buildeh append(text): 

bullder append( </td></tr><tr><tdbgcolor: ): 

buU ̄r:append(this._doneBackColor): 

return builder.toString()‘: 

} 

该方法使用文本和百分比,返回包含两行的HTML表。顶 

行仅显示文本,底行分为两个单元格,分别带有不同的颜色。 

标记字符串是使用JavaScript版本的Microsoft.NET Frame— 

work StifngBuilder对象构建的。JavaScfipt StringBuilder对象是 

在系统命名空间中定义的,其编程接口类似于.NET Framework 

接口。向StringBuilder的内部缓冲区发送文本,然后使用 

toStifng方法输出文本。 

Samples.GaugeBar类具有一个generateMarkup方法,以及 

“已完成”和“未完成”区域的背景色、附带文本的前景色等 

属性。由于性能方面的原因,此类作为单例来使用。这个类不 

是很大,但每次需要更新进度条时,仍然不必为其创建新实 

例。因此,可以为该类定义一个静态实例,并添加一些静态方 

法和属性: 

Samples.GaugeBar registerClass( Samples.Gauge8ar ): 

Samples.GaugeBar.

_

staticlnstance= 

new Samples.GaugeBar(): 

SamPles.GaugeBar.generateMarkup=function(text,perc){ 

return Samples.GaugeBar.

_

staticlnstance.generateMarkup 

(text,perc): 

要更改颜色,请执行以下操作: 

Samples.GaugeBar.set

_

DoneBackColor( #ffOOee ): 

Samples.GaugeBar.set

_

TodoBackColor( #ffccee ): 

同样,可以通过为表的“已完成”单元格定义开始边框样 

式,添加美观的3D效果,实现代码如下: 

if(this. 仟ect3D) 

builder.append f.sty le: border:outset white 2px;一): 

通过创建一个类来公开功能可大大提高JavaScript编程的 

可管理性。Microsoft客户端AJAX库是一个很大的进步,因为 

使用此库编写复杂的JavaSefipt代码会轻松得多。大多数AJAX 

专业人员可能都同意这一点:要实现强大的AJAX编程,必须 

具备更丰富的JavaScfipt功能。 

(微软MSDN中文网站供稿) 

(收稿日期:2007年11月18日) 


本文标签: 任务 请求 客户端 方法 服务器