论坛中已有不少关于.NET ADF中AJAX的精彩讲解,但切入点有些抽象。本文试图从一个初学者的角度着眼,从AJAX讲起,由浅入深,去掉不必要的技术细节,使大家对.NET ADF中AJAX有清晰的理解。再辅以前人的教程,由此能更充分地掌握.NET ADF中AJAX的工作原理。
经常在论坛上看到很多朋友问:为什么我已经创建了一个callbackresult,但是浏览器中却没有任何反应?或者我已经把ADF控件放到了 UpdatePanel里,但为什么还是达不到我想要的效果?9.3版本的.NET Web ADF发布近2年的时间里,ESRI从来没有说过:“很抱歉,我们ADF中的AJAX工作流程有时候可能会出现一点问题”,这证明.NET Web ADF中的AJAX还是足够可靠的,事实是问题只可能出现在我们的代码里。甚至在你根本还没有弄清如何来正确使用ADF中的AJAX时,就模仿别人的代码,寄希望于你也不知道为什么要创建的一个callbackresult就能够好好的为你工作,这不应该是经常和只知道循规蹈矩的机器打交道的人所做的事情。
既然你已经选择了.NET ADF,并想利用好其中的AJAX能力,那么弄清楚它的原理应该是必要的,可以从本文第二部分开始阅读;但也许你是一个ArcGIS Server的新手,甚至是ASP.NET的新手,不得不在短时间内完成你手上的GIS任务,并不关心它的原理,那你可以阅读本文的前两部分或者只是第二部分。(如果你是一个网页开发的新手,那么请自行学习HTML+CSS+Javascript)
第一部分:1分钟AJAX时间
典型的,ASP.NET 2.0之前的网页,当客户端发送请求的时候,比如点击“开始处理”按钮,会将整个页面信息提交到服务器上这个页面对应的类进行处理(发送的请求叫做 postback),这个页面类处理完成后将结果(包括页面原有的信息)输出成浏览器能够识别的HTML+CSS+Javascript返回客户端,你才会看到结果页面。提交请求后到结果页面呈现前,你的鼠标会呈沙漏状,无法操作页面;结果页面呈现的一瞬间,你会看到整个页面完全刷新一次。AJAX的出现就是为了解决这个糟糕的过程,有了AJAX本领的页面,当你点击“开始处理”按钮后,你依然能够对页面其他部分进行操作,结果呈现的时候也不会刷新全部页面,而只是悄悄修改需要改变的部分。目前互联网上大多数页面都有AJAX能力,但不要迷恋AJAX,它只是个效果的名称而已,你需要感谢的是 ASP.NET中实现AJAX的两个功臣:ASP.NET 2.0时候出现的Client Callback和ASP.NET 2.0的一个扩展ASP.NET AJAX。但也不要迷恋ASP.NET,因为在最下面真正干苦力活的是Javascript里的XMLHttpRequest对象,前两者是对其不同程度的封装。
最后重复一下,Client Callback和ASP.NET AJAX是并列关系,根据你的需要取其一作为你实现AJAX的手段即可。
第二部分:如何正确使用CallbackResult
任何想正确使用CallbackResult的人应该都不会拒绝我先解释一下究竟什么是CallbackResult,那就先来看看它是什么。
在只有一个Map Control和Toolbar Control页面中,无论你如何操作地图,或者使用Toolbar上的按钮和工具与地图交互,都不会刷新整个页面,这证明所有Web ADF的控件具有AJAX能力,因为其他控件也是如此。Web ADF中的Server Control是如何实现这一点呢?无疑是利用了Client Callback(9.2版本和以上可用)或者ASP.NET AJAX(9.3版本和以上可用)。究竟怎么利用?这个问题先别急,让我们深入一点点。
针对Web ADF控件,先来看一个普通的操作:鼠标拉框放大地图。它的整个完成过程是这样的:用户在客户端的地图上(其实是ESRI自己封装的名为Map的Javascript对象,属于Web ADF Javascript中的内容)用鼠标拉一个框,松开鼠标的时候地图对象会将一些信息,比如这个框框的范围等,发送到服务器端,服务器端对应的Map控件会根据服务器端当前Map的范围和框框的范围做计算,保留新的范围在服务器的Map控件上。服务器端的过程容易完成,但这些服务器端控件还需要负责任地将这些结果同步到客户端去,否则客户端看不到任何变化。在这个例子中服务器端将结果同步到客户端的过程是,由地图服务输出新的地图图像(如果是缓存地图服务,则给出新范围的切片)传送给客户端。如下图所示:


ADFAJAX1.jpg
如果不细心可以直接看下一段。如果细心,你可能会问为什么一个放大操作,上图会出现那么多回合的请求和响应?事实是,放大地图这个操作里会有两次请求/响应过程。请求1:传输框框范围到服务器;响应1:返回放大后的新地图范围给客户端;(客户端的地图控件收到响应1后立即再次发出)请求2:根据新范围问服务器端地图控件所要结果地图图片;响应2:传输地图图片给客户端。一个放大过程才算完成。结论:对Web ADF控件做的一个操作,很可能会引起不只一次请求和响应,但我们只关心且只需要关心最初的请求和最终的结果,因为其他过程Web ADF控件会自动替我们完成。
任何对Web ADF控件的操作都是和上面的过程一样,客户端的Web ADF Javascript对象发送请求,服务器端相应的控件处理结果并通过“响应”(伴随着一系列对我们透明的后续请求和响应)来将结果同步到客户端去,这个 “响应”就是我们的主角:ADF中的CallbackResult对象(实际上是JSON字符串)。它是由服务器端的Web ADF控件生成,客户端的特定Web ADF Javascript函数负责解析的。不管生成的CallbackResult长什么样,客户端的负责解析的函数名字都叫做:processCallbackResult()。如下图所示:

ADFAJAX2.jpg
为了逻辑上的需要,比如一个动作对应一个CallbackResult,于是就有了上图的CallbackResultCollection。在服务器端,每个Web ADF Control都有一个Callbackresults属性(CallbackResultCollection),为什么需要这样呢?必须记住一点:“谁请求,谁管理”,即(客户端)发起请求的那个控件(在服务器端所对应的控件)负责传回CallbackResults。CallbackResults传回到客户端后,Web ADF Javascript中的processCallbackResult()函数就会自动对其进行解析。所以开发人员所需要做的工作就是,将服务器端所有产生的CallbackResults交付给负责带回CallbackResults的那个控件——加入控件的CallbackResults属性中。你并不需要关心CallbackResults到了客户端是如何解析的,因为这一切都由processCallbackResult()自动完成。因为请求可能由任何一个控件触发,所以每个Web ADF控件都有一个CallbackResults属性。

比如,运行时动态给Map控件添加了新的地图服务。在这个过程中,Map控件会发送请求,那么自然是服务器端的Map控件负责将CallbackResults带回客户端。而页面中的TOC控件为了反映出这个变化,在服务器端就要对其进行刷新,刷新这个动作就会使TOC控件自身产生CallbackResults。记住“谁请求,谁管理”,Map请求,Map管理。如果不将TOC刷新的结果通过Map的CallbackResults带回客户端,那么页面上的TOC是不会有任何变化的。正确的做法是:

... <new map resource item added>
Toc1.Refresh();
Map1.CallbackResults.copyFrom(Toc1.CallbackResults);

整个过程如下图所示:

ADFAJAX3.jpg
不是所有的Web ADF变化都需要我们手动处理,下面列出了一些Web ADF控件之间的内在关系:
  • 已经绑定到Map的Toolbar控件上,任何command或tool执行过程中,会自动将Map的CallbackResults拷贝到Toolbar的CallbackResults中。所以如果在自定义command或tool里对Map做了修改,则不需要将其产生的CallbackResults拷贝给负责带回响应的Toolbar控件;
  • 已经绑定到Map的TOC控件,NodeChecked事件会自动将Map产生CallbackResults拷贝给TOC控件;当Map和TOC中包含有可见性依赖比例尺变化而变化的图层时,ScaleChanged事件会将Toc的CallbackResults拷贝给Map控件;
  • ScaleBar绑定到Map后,ScaleBar的PreRender事件会将ScaleBar的CallbackResults拷贝给Map控件;
  • MapcopyrightText控件绑定到Map后,添加或移除map resource item会将MapcopyrightText的CallbackResults拷贝给Map控件;
  • Magnifier绑定到Map后,改变Map的显示范围或初始化tiling scheme时,会将Magnifier的CallbackResults拷贝给Map控件;
  • FloatingPanel的Refresh()方法会将其中所有子控件CallbackResults拷贝到自身的CallbackResults中;
  • TaskResults绑定到Map后,执行一个task会将Map的CallbackResults拷贝给TaskResults控件,TaskResults的CallbackResults随后会自动拷贝给这个执行的task。所以如果你手动将Map的CallbackResults拷贝给这个task的CallbackResults的话,将会产生重复的结果;
  • TaskResults控件有预定义的ContextMenus。这些右键菜单项的ItemClicked事件会将TaskResults和Map的CallbackResults自动拷贝给ContextMenu。
    这些内在关系在下面的Web ADF控件一览图中可见一斑。除了以上事件,其他情况下的CallbackResults都需要你自己将其添加到负责带回响应的控件中。

    ADFAJAX4.jpg
    另外,还可以在服务器端自定义CallbackResult,来达到修改客户端非Web ADF控件或者执行一段Javascript代码的目的。自定义CallbackResult可已通过其构造函数或静态方法来实现,这里不再赘述:

    ADFAJAX5.jpg
    当然,别忘了将自己创建的CallbackResults拷贝到正确的控件中。
    至此,如何正确使用CallbackResult的问题已经讲完了,这一切叫做Web ADF中callback result框架。它给我们提供了统一的编程体验,而你则可以根据自己的需要和编程经验来选择使用Client Callback还是ASP.NET AJAX。最后郑重提醒一点:如果使用了ASP.NET AJAX方式,在页面中强烈建议让所有Web ADF控件远离UpdatePanel。因为它们本身就能够注册到ScriptManager中,在完全刷新自己的情况下即可完成必要的内容更新。如果你将Web ADF控件放在UpdatePanel中,虽然能够正常工作,但刷新整个Web ADF控件。
    第三部分:深入理解Web ADF中的AJAX
    现在来看看前面提出的问题:Web ADF究竟是如何利用ASP.NET中的Client Callback或是ASP.NET AJAX的?首先明确一点,Web ADF控件只能使用一种异步通信方式,要么是Client Callback,要么是ASP.NET AJAX。如果页面中有ScriptManager控件,就意味着你选择了ASP.NET AJAX方式进行异步通信,那么Web ADF控件的实际运行中也会采用这种方式;反之如果页面中没有ScriptManager控件,那么Web ADF实质上会采用Client Callback方式来进行异步通信。下面分别通过几个场景来看看两种方式的实际操作区别:
    如果采用了Client Callback方式:
    • 场景一:Web ADF控件来产生请求和响应,客户端processCallbackResult()来处理响应
      这个过程就是第二部分中假设的几个场景,请求由一个Web ADF控件产生,服务器端进行处理,也可在此时创建自定义CallbackResult来对页面中其他控件进行修改,最后将这些CallbackResults赋给正确的控件即可;响应带回客户端后,由Web ADF Javascript的processCallbackResult()函数进行解析。如图所示:

      ADFAJAX6.jpg
    1. 场景二:非Web ADF控件来产生请求和响应,客户端processCallbackResult()来处理响应
      1. 比如页面实现了ICallbackEventHandler接口,请求由页面中的一个HTML Button发出,在RaiseCallbackEvent()方法中将所有响应,也包括非Web ADF控件的,都构建成CallbackResult的结构,并集中到CallbackResultCollection中,然后在GetCallbackResult()方法中返回CallbackResultCollection.ToString(),客户端处理callback响应的js函数指定为Web ADF Javascript中的processCallbackResult()函数,则省去了自己解析callback响应的麻烦。如图所示:

        ADFAJAX7.jpg
      2. 场景三:非Web ADF控件来产生请求和响应,客户端自己处理响应
        1. 除了在客户端使用自定义的函数来解析包括CallbackResult结构在内的服务器响应外,其余和场景二是相同的。这种情况下你需要知道CallbackResult的JSON字符串中,使用“:::”分隔符来分隔参数,使用“^^^”分隔符来分隔多个CallbackResult,此外processCallbackResult()的定义可在X:\Inetpub\wwwroot\aspnet_client\ESRI\WebADF\JavaScript\ESRI.ADF.System.debug.js中找到。除非你已经有一套处理callback响应的js框架,否则还是推荐在客户端使用processCallbackResult()来进行响应处理。如图所示:

          ADFAJAX8.jpg
          如果采用了ASP.NET AJAX方式:
        2. 场景一:Web ADF控件触发partial postback
          1. 此情形和Client Callback方式场景一类似,发送请求的Web ADF控件是ASP.NET AJAX中的trigger,它的CallbackResults注册为ScriptManager的一个data item返回客户端。如下图所示:

            ADFAJAX9.jpg
          2. 场景二:非Web ADF控件触发partial postback
            1. trigger是非Web ADF控件时,更新Web ADF控件内容的方法有三种:UpdatePanels,data items或dynamic script blocks。如果将Web ADF控件放入UpdatePanel,则根据UpdatePanel的特性,会刷新里面整个Web ADF控件,这是不必要的,Web ADF控件所需要更新的内容仅仅是服务器端产生的CallbackResults,由客户端的processCallbackResult()即可完成,而不需要从头到尾的洗心革面。所以CallbackResults可以当做data items或dynamic script blocks来插入到partial postback的响应中。
              作为dynamic script blocks时: 首先要使用dynamic script blocks,页面中至少要有一个UpdatePanel(空的也可)。只需要在客户端调用ESRI.ADF.System.processCallbackResult() 来处理服务器端产生的CallbackResults(作为该函数的参数),如下:

              . . .
              string jsProcessCallbackResult = string.Format("ESRI.ADF.System.processCallbackResult('{0}');",Map1.CallbackResults.ToString().Replace("\\","\\\\"));
              
              ScriptManager.RegisterClientScriptBlock(Page,sender.GetType(),"changeextent",string.Format(jsProcessCallbackResult,Map1.CallbackResults),true);

              此方式的优点:Web ADF控件不需要完全刷新,客户端响应解析也不需要自己来写;缺点:注册的dynamic script blocks会保存在客户端内存中,最终可能会导致浏览器内存超出限制。
              作为data items时: ScriptManager中注册的data items可以在服务器上以(JSON)字符串形式打包传到客户端进行处理。客户端接收到服务器端的异步postback响应后,但在进行任何局部内容的修改前,会触发客户端的pageLoading事件。这样就可以利用PageRequestManager捕捉到这个事件,从而将注册成data items的CallbackResults交给processCallbackResult() 来处理。过程如下图所示:

              ADFAJAX10.jpg
              在服务器端将CallbackResult注册成data item:

              ScriptManager1.RegisterDataItem(Page,Map1.CallbackResults.ToString(),false);
              监听pageLoading事件:

              <script>                                
              Sys.Application.add_init(onInitFunction);
                                                  
              // Called once during application initialization
              function onInitFunction()
              {
                 Sys.WebForms.PageRequestManager.getInstance().add_pageLoading(AsyncResponseHandler);
              }
              
              // Called whenever a response to a partial postback is processed on the client
              function AsyncResponseHandler(sender,args) 
              {
                 var dataItems = args.get_dataItems();
                 if (dataItems['__Page'] != null)
                    ESRI.ADF.System.processCallbackResult(dataItems['__Page']);
              }
              </script>        

    ArcGIS Server .NET ADF中的AJAX之深入浅出/CallbackResult详解的更多相关文章

    1. 应用程序关闭时的iOS任务

      我正在构建一个应用程序,通过ajax将文件上传到服务器.问题是用户很可能有时不会有互联网连接,并且客户希望在用户重新连接时安排ajax调用.这可能是用户在离线时安排文件上传并关闭应用程序.应用程序关闭时可以进行ajax调用吗?

    2. Perfect:Swift 语言服务器端软件框架简介

      Perfect:Swift语言服务器端软件框架Perfect开源项目参与Perfect开发Slack在线协同Perfect:Swift语言服务器端软件框架Perfect是一组完整、强大的工具箱、软件框架体系和Web应用服务器,可以在Linux、iOS和macOS上使用。Perfect内建整套工具集,因为无论是客户端还是服务器都能够在此基础之上用同一种计算机语言Swift进行程序开发,因此能够为软件工程师大幅提高工作效率。使用PerfectSwift语言兼容性目前本项目主干版本基于Xcode8GMrelea

    3. 令世人惊叹的服务器端 Swift!

      接下来我们将会聊一聊这些框架的优缺点,并且介绍如何入门服务器端Swift开发。(其中的几个框架仅支持Swift3!我打算以一个旁观者的角度来讨论服务端Swift开发,比较各个框架以及服务端Swift开发所处的位置。应用程序,接着通过现场编码演示如何部署服务端Swift开发环境,编写一个“Hello,World!”随着12月份Swift正式开源,开发者已经开始尝试服务端开发,使用Swift构建web应用程序。不难看出服务端Swift开发极具潜力。他们的目标是将Swift做的和Rails一样。

    4. android – Phonegap本地构建 – jquery ajax错误:readystate 0 responsetext status 0 statustext error

      解决方法您是否在索引文件中包含了内容安全元标记?

    5. 基于Android网络的模拟器Manymo,它是如何工作的?

      任何线索都会有所帮助.解决方法他们似乎正在使用noVNC客户端.在服务器端,他们可以使用任何具有VNC支持的仿真器.

    6. Ajax简单的异步交互及Ajax原生编写

      一提到异步交互大家就会说ajax,仿佛ajax这个技术已经成为了异步交互的代名词.那下面将研究ajax的核心对象

    7. Ajax跨域问题的解决办法汇总(推荐)

      本文给大家分享多种方法解决Ajax跨域问题,非常不错具有参考借鉴价值,感兴趣的朋友一起学习吧

    8. ajax编写简单的登录页面

      这篇文章主要为大家详细介绍了ajax编写简单登录页面的具体代码,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

    9. ajax从JSP传递对象数组到后台的方法

      今天小编就为大家分享一篇ajax从JSP传递对象数组到后台的方法,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧

    10. 解决ajax返回验证的时候总是弹出error错误的方法

      这篇文章主要介绍了解决ajax返回验证的时候总是弹出error错误的方法,感兴趣的小伙伴们可以参考一下

    随机推荐

    1. xe-ajax-mock 前端虚拟服务

      最新版本见Github,点击查看历史版本基于XEAjax扩展的Mock虚拟服务插件;对于前后端分离的开发模式,ajax+mock使前端不再依赖后端接口开发效率更高。CDN使用script方式安装,XEAjaxMock会定义为全局变量生产环境请使用xe-ajax-mock.min.js,更小的压缩版本,可以带来更快的速度体验。

    2. vue 使用 xe-ajax

      安装完成后自动挂载在vue实例this.$ajaxCDN安装使用script方式安装,VXEAjax会定义为全局变量生产环境请使用vxe-ajax.min.js,更小的压缩版本,可以带来更快的速度体验。cdnjs获取最新版本点击浏览已发布的所有npm包源码unpkg获取最新版本点击浏览已发布的所有npm包源码AMD安装require.js安装示例ES6Module安装通过Vue.use()来全局安装示例./Home.vue

    3. AJAX POST数据中文乱码解决

      前端使用encodeURI进行编码后台java.net.URLDecoder进行解码编解码工具

    4. Koa2框架利用CORS完成跨域ajax请求

      实现跨域ajax请求的方式有很多,其中一个是利用CORS,而这个方法关键是在服务器端进行配置。本文仅对能够完成正常跨域ajax响应的,最基本的配置进行说明。这样OPTIONS请求就能够通过了。至此为止,相当于仅仅完成了预检,还没发送真正的请求呢。

    5. form提交时,ajax上传文件并更新到&lt;input&gt;中的value字段

    6. ajax的cache作用

      filePath="+escape;},error:{alert;}});解决方案:1.加cache:false2.url加随机数正常代码:网上高人解读:cache的作用就是第一次请求完毕之后,如果再次去请求,可以直接从缓存里面读取而不是再到服务器端读取。

    7. 浅谈ajax上传文件属性contentType = false

      默认值为contentType="application/x-www-form-urlencoded".在默认情况下,内容编码类型满足大多数情况。在这里,我们主要谈谈contentType=false.在使用ajax上传文件时:在其中先封装了一个formData对象,然后使用post方法将文件传给服务器。说到这,我们发现在JQueryajax()方法中我们使contentType=false,这不是冲突了吗?这就是因为当我们在form标签中设置了enctype=“multipart/form-data”,

    8. 909422229_ajaxFileUpload上传文件

      ajaxFileUpload.js很多同名的,因为做出来一个很容易。我上github搜AjaxFileUpload出来很多类似js。ajaxFileUpload是一个异步上传文件的jQuery插件传一个不知道什么版本的上来,以后不用到处找了。语法:$.ajaxFileUploadoptions参数说明:1、url上传处理程序地址。2,fileElementId需要上传的文件域的ID,即的ID。3,secureuri是否启用安全提交,默认为false。4,dataType服务器返回的数据类型。6,error

    9. AJAX-Cache:一款好用的Ajax缓存插件

      原文链接AJAX-Cache是什么Ajax是前端开发必不可少的数据获取手段,在频繁的异步请求业务中,我们往往需要利用“缓存”提升界面响应速度,减少网络资源占用。AJAX-Cache是一款jQuery缓存插件,可以为$.ajax()方法扩展缓存功能。

    10. jsf – Ajax update/render在已渲染属性的组件上不起作用

      我试图ajax更新一个有条件渲染的组件。我可以确保#{user}实际上是可用的。这是怎么引起的,我该如何解决呢?必须始终在ajax可以重新呈现之前呈现组件。Ajax正在使用JavaScriptdocument.getElementById()来查找需要更新的组件。但是如果JSF没有将组件放在第一位,那么JavaScript找不到要更新的内容。解决方案是简单地引用总是渲染的父组件。

    返回
    顶部