深入Atlas系列:综合示例(1) - 调用服务器端方法时直接获得客户端具体类型

简介:
  在使用ASP.NET AJAX时,大家对于返回服务器端的复杂类型的情况经常会遇到问题。Dflying兄写了一篇文章来说明在如何在客户端得到Sys.Preview.Data.DataTable对象的文章(详见《 现存问题以及解决方案:在ASP.NET AJAX客户端得到服务器端的DataTable 》),但是这种方法似乎从“美学”角度来说并不优雅,而且是针对了DataTable这一个问题所提出的解决方案。

  Dflying兄的文章里提到,如果直接使用方法的得到的结果,会出现问题,如下:
function  cb_getDataTable(result)
{
    
var  contentBuilder  =   new  Sys.StringBuilder();
    
for  ( var  i  =   0 ; i  <  result.get_length();  ++ i)
    {
        contentBuilder.append(
" <strong>Id</strong>:  " );
        contentBuilder.append(result.getRow(i).getProperty(
" Id " ));
        contentBuilder.append(
"  <strong>Name</strong>:  " );
        contentBuilder.append(result.getRow(i).getProperty(
" Name " ));
        contentBuilder.append(
" <br /> " );
    }
    
    $get(
" result " ).innerHTML  =  contentBuilder.toString();
}

  Dflying兄对于这一问题的解释是“DataTableConverter”中出现致命Bug。不过, DataTableConverter中的确存在着“不可救药”的低级Bug并不是客户端没有得到Sys.Preview.Data.DataTable 的原因。事实上如果不对ASP.NET AJAX做扩展的话,是无法直接获得一个客户端具体类型的。在ASP.NET AJAX中,从服务器端到客户端的只是一个JSON字符串表示的对象,它又怎么可能从一开始就是一个 Sys.Preview.Data.DataTable对象呢?因此从Dflying兄的解决方案里可以看出,事实上在客户端使用代码构造了一个 Sys.Preview.Data.DataTable对象。

  如果我们需要改进这一点,就必须对于ASP.NET AJAX进行扩展。幸运的是,ASP.NET AJAX的可扩展能力非常好,这要归功于JavaScript语言和prototype机制。


1、提出解决方案:

   我们既然需要直接得到一个客户端的具体类型,也就是说,在得到JSON表示的对象之后,还必须做一些额外的事情。而这样的事情应该是服务器端控制的,也 就是说在序列化的结果之后,应该将额外的工作“一并输出”。不过可惜的是,在客户端只能输出普通的数据类型,而不能输出“函数”。那么该怎么办呢?

  不过还好,虽然不能输出函数,但是在脚本代码中,函数也是通过字符串的形式表示的,那么我们就用字符串来表示提供“额外工作”的函数吧。于是在这里,我设计了如下协议:
  1. 在序列化输出时,如果需要有额外的工作,在对象中以字符串输出一个“__getRealObject”函数,它的形式是 “function(o){ ... }”,其中参数o就是“__getRealObject”函数所在的对象,而“__getRealObject”函数的返回值则会将该对象替换掉。
  2. 扩展ASP.NET AJAX客户端访问服务器的基础结构,以利用对象输出的“__getRealObject”。
  可以发现,我们在“__getRealObject”函数中其实可以定义“任意工作”,这个作用是非常大的,比如我们能够在客户端构造一个有相互引用的复杂对象。那么我们就开始实现吧:


2、为DataTable定义JavaScriptConverter并使用:
namespace  Jeffz
{
    
public   class  DataTableConverter : Microsoft.Web.Preview.Script.Serialization.Converters.DataTableConverter
    {
        
public   override  IDictionary < string object >  Serialize( object  obj, Microsoft.Web.Script.Serialization.JavaScriptSerializer serializer)
        {
            IDictionary
< string object >  result  =    base .Serialize(obj, serializer);

            
string  dataArray  =  result[ " dataArray " ].ToString();
            
if  (dataArray[dataArray.Length  -   1 ==   ' ) ' )
            {
                // 顺便fix掉bug
                result[
" dataArray " =  dataArray.Substring( 0 , dataArray.Length  -   1 );
            }

            result[
" __getRealObject " =   " function(o) { return new Sys.Preview.Data.DataTable(o.columns, eval(o.dataArray)); } " ;

            
return  result;
        }
    }
}

  可以看到,我们定义的DataTableConverter继承了Feature-add(即Beta1的Value-add)程序集中的 Microsoft.Web.Preview.Script.Serialization.Converters.DataTableConverter, 并覆盖了Serialize方法。可以看到,我们在获得了原来的结果之后,首先改变“dataArray”的值,这个目的是修改原来 DataTableConverter的bug。然后再增加一个“__getRealObject”,添加一个函数,这些还是相当简单的。

  然后我们只需修改web.config的配置,让它使用我们的DataTableConverter。如下:
< jsonSerialization  maxJsonLength ="500000000" >
    
< converters >
        
< add  name ="DataTableConverter"  type ="Jeffz.DataTableConverter" />
    
</ converters >
</ jsonSerialization >


3、扩展客户端访问服务器基础结构:

  “官方”的扩展方式是自定义WebRequestExecutor,但是在这里我使用了一个另一个方法。那么直接来看代码吧:
Sys.Net.WebRequestExecutor.prototype._get_object  =  Sys.Net.WebRequestExecutor.prototype.get_object;

Sys.Net.WebRequestExecutor.prototype._getRealObject 
=   function (obj)
{
    
if  (obj  &&   typeof (obj)  ==  'object'  &&  
        
! Array.isInstanceOfType(obj)  &&   ! Date.isInstanceOfType(obj))
    {
        
for  (m  in  obj)
        {
            
var  value  =  obj[m];
            obj[m] 
=   this ._getRealObject(value);
        }
        
        
var  strMethod  =  obj[ " __getRealObject " ];
        
if  (strMethod)
        {
            
delete  obj.__getRealObject;
            eval(
" var method =  "   +  strMethod);
            
return  method(obj);
        }
    }
    
    
return  obj;
}

Sys.Net.WebRequestExecutor.prototype.get_object 
=   function ()
{
    
var  obj  =   this ._get_object();
    
return   this ._getRealObject(obj);
}

Sys.Application.notifyScriptLoaded();

  我们在这里改变了Sys.Net.WebRequestExecutor的prototype中的get_object方法,在返回给客户端 之前需要进行进一步的处理(使用_getRealObject函数)。我们会首先递归地处理对象中的每个值,然后再对于这个对象应用 “__getRealObject”方法,当然前提是用户给出了这个定义。


4、使用效果:

  这个例子是在Dflying兄上述文章中的例子基础上修改完成。首先是HTML:
< asp:ScriptManager  ID ="ScriptManager1"  runat ="server" >
    
< Scripts >
        
< asp:ScriptReference  Assembly ="Microsoft.Web.Preview"  Name ="Microsoft.Web.Resources.ScriptLibrary.PreviewScript.js"   />
        
< asp:ScriptReference  Path ="js/ExecutorExtention.js"   />
    
</ Scripts >
</ asp:ScriptManager >
< input  id ="btnGetDataTable"  type ="button"  value ="Get DataTable"  onclick ="return btnGetDataTable_onclick()"   />
< div  id ="result" ></ div >

  然后是JavaScript:
function  btnGetDataTable_onclick() 
{
    PageMethods.GetDataTable(cb_getDataTable);
}

function  cb_getDataTable(result)
{
    // 请注意,result已经是Sys.Preview.Data.DataTable对象了
    
var  contentBuilder  =   new  Sys.StringBuilder();
    
for  ( var  i  =   0 ; i  <  result.get_length();  ++ i)
    {
        contentBuilder.append(
" <strong>Id</strong>:  " );
        contentBuilder.append(result.getRow(i).getProperty(
" Id " ));
        contentBuilder.append(
"  <strong>Name</strong>:  " );
        contentBuilder.append(result.getRow(i).getProperty(
" Name " ));
        contentBuilder.append(
" <br /> " );
    }
    
    $get(
" result " ).innerHTML  =  contentBuilder.toString();
}

  当然,Page Method很重要:
[System.Web.Services.WebMethod]
[Microsoft.Web.Script.Services.ScriptMethod]
public   static  DataTable GetDataTable()
{
    DataTable myDataTable 
=   new  DataTable();
    myDataTable.Columns.Add(
new  DataColumn( " Id " typeof ( int )));
    myDataTable.Columns.Add(
new  DataColumn( " Name " typeof ( string )));

    
for  ( int  i  =   0 ; i  <   10 ++ i)
    {
        DataRow newRow 
=  myDataTable.NewRow();
        newRow[
" Id " =  i;
        newRow[
" Name " =   string .Format( " Name {0} " , i);

        myDataTable.Rows.Add(newRow);
    }

    
return  myDataTable;
}

  最后来看一下效果吧:



5、不足之处:

   事实上,这个解决方案也不够完美,如果我们的服务器方法返回的是一个DataTable数组呢?我们并没有遍历数组的每一个元素以查看数组内的元素。另 外,如果使用了数组,则会发现数组内的“每个对象”都会存在一个__getRealObject方法,即使他们都是相同的类型。那么应该怎么做呢?可能这 就需要朋友们根据实际情况来做些改进了吧。:)



本文转自 jeffz 51CTO博客,原文链接:http://blog.51cto.com/jeffz/60655,如需转载请自行联系原作者

相关文章
|
8天前
|
监控
查看服务器/IIS日志、log、访问信息基本方法
除了手动查看,你也可以使用日志分析工具,如Log Parser、AWStats等,这些工具可以帮助你更方便地分析日志数据。
5 1
|
15天前
|
网络协议 网络架构
【网络编程入门】TCP与UDP通信实战:从零构建服务器与客户端对话(附简易源码,新手友好!)
在了解他们之前我们首先要知道网络模型,它分为两种,一种是OSI,一种是TCP/IP,当然他们的模型图是不同的,如下
|
6天前
|
安全 网络协议 网络安全
SSL(Secure Sockets Layer)是一种安全协议,用于在客户端和服务器之间建立加密的通信通道。
SSL(Secure Sockets Layer)是一种安全协议,用于在客户端和服务器之间建立加密的通信通道。
|
6天前
|
网络协议 安全 Python
我们将使用Python的内置库`http.server`来创建一个简单的Web服务器。虽然这个示例相对简单,但我们可以围绕它展开许多讨论,包括HTTP协议、网络编程、异常处理、多线程等。
我们将使用Python的内置库`http.server`来创建一个简单的Web服务器。虽然这个示例相对简单,但我们可以围绕它展开许多讨论,包括HTTP协议、网络编程、异常处理、多线程等。
|
9天前
|
Java 数据格式
Java面试题:简述Java Socket编程的基本流程,包括客户端和服务器的创建与通信。
Java面试题:简述Java Socket编程的基本流程,包括客户端和服务器的创建与通信。
14 0
|
15天前
|
Web App开发
软件开发常见流程之移动端调试方法,利用Chrome(谷歌浏览器)的模拟手机调试,搭建本地Web服务器,手机和服务器在一个局域网,通过手机访问服务器,使用服务器,利用ip实现域名访问
软件开发常见流程之移动端调试方法,利用Chrome(谷歌浏览器)的模拟手机调试,搭建本地Web服务器,手机和服务器在一个局域网,通过手机访问服务器,使用服务器,利用ip实现域名访问
|
16天前
|
前端开发 JavaScript Java
文本----简单编写文章的方法(中),后端接口的编写,自己编写好页面就上传到自己的服务器上,使用富文本编辑器进行编辑,想写好一个项目,先分析一下需求,再理一下实现思路,再搞几层,配好参数校验,lomb
文本----简单编写文章的方法(中),后端接口的编写,自己编写好页面就上传到自己的服务器上,使用富文本编辑器进行编辑,想写好一个项目,先分析一下需求,再理一下实现思路,再搞几层,配好参数校验,lomb
|
16天前
|
JavaScript
文本----简单编写文章的方法(上),自己编写好页面就上传到自己的服务器上,使用富文本编辑器进行编辑
文本----简单编写文章的方法(上),自己编写好页面就上传到自己的服务器上,使用富文本编辑器进行编辑
|
17天前
|
网络协议 网络安全
使用NetAssist网络调试助手在单台计算机上配置TCP服务器和客户端
使用NetAssist网络调试助手在单台计算机上配置TCP服务器和客户端
35 0
|
17天前
|
弹性计算 供应链 并行计算
阿里云ECS包年包月、按量付费、抢占式实例、节省计划和预留实例券付费类型详细说明
阿里云服务器计费多样化:包年包月适合长期服务,预付费且划算;按量付费适合短期项目,后付费、按小时结算;抢占式实例享折扣但可能被释放,适合无状态任务;预留实例券抵扣按量付费账单;节省计划提供承诺使用量的折扣,适用于资源用量稳定或周期性变化的业务。
22 0