第6章数据传输的标准化解析.docx
《第6章数据传输的标准化解析.docx》由会员分享,可在线阅读,更多相关《第6章数据传输的标准化解析.docx(16页珍藏版)》请在冰豆网上搜索。
第6章数据传输的标准化解析
第6章数据传输的标准化
ExtJS除了组件丰富外,另外一个特点就是数据传输的标准化。
那么,这样做,有什么好处?
为什么要这样做呢?
本章将为你解答这些问题。
在搞清楚这些问题之后,就要考虑怎么去实现了。
本章将通过简单的CMS系统来演示数据传输的标准化的基本的实现思路。
1
2
3
4
5
6
6.1标准化的数据传输是什么
在本书所说的标准化的数据传输主要是指,使用代理提交数据时,格式必须统一化标准化,而服务器端在处理提交的数据后,返回的数据格式必须是标准化的数据。
6.2为什么要实现数据传输的标准化
假设要使用ExtJS开发一个非常庞大的复杂的系统,在项目组里有许多开发人员,其中,开发人员A习惯以A方式提交和返回数据,而开发人员B则习惯以B方法提交和返回数据,而开发人员C则需要根据开发人员A和B的数据请求和数据格式返回数据,那么,开发人员C就必须根据开发人员A和B各自喜好的方式去获取提交数据和返回不同格式的数据。
可以想象得到,开发人员C是多么的悲催,估计杀人的想法都有了。
姑且勿论是否存在开发人员C这样的悲情人物,当系统开发完成,进入后期维护的时候,维护人员面对的就是一堆不同格式的提交数据和返回数据,这无疑大大增加了维护成本。
如果以上两种情况,还不足以说明问题的话,那么,当公司需要针对某个客户的需求,需要修改开发平台,进行迁移的时候,就得根据不同格式去进行迁移,这无疑也增大了迁移的开发成本。
因此,实现数据传输的标准化是很有必要的。
6.3标准化数据传输的好处
根据6.1节的描述,标准化数据传输的好处之一就是,服务器端的数据处理可以实现统一化标准化,无论客户端提交的数据是什么,只需要根据标准化统一化的处理过程,就能把数据分离出来并进行相应处理。
通过数据处理的标准化统一化,就可以大大减少重复代码,提高代码的可重用性,而这,无疑是大大的利好。
重复代码的减少,无疑会提高代码的可维护性,而这也是利好之一。
由于客户端接收的是以标准格式的返回的数据,因而,无论服务器端平台怎么变化,只要以这种标准化格式返回数据,客户端代码不需要做任何修改,就能正确处理返回的数据。
因而,标准化的数据传输,对于项目开发平台的迁移,也是有相当大的好处的。
6.4如何实现标准化
既然实现数据传输的标准化是好的,那么如何去实现呢?
这,首先得从ExtJS的数据传输方式说起。
6.4.1ExtJS的主要数据传输方式
ExtJS主要的数据传输方式有Ajax、JsonP、表单和代理这4种方式。
JsonP是用来处理跨域请求的,由于跨域请求会受到另一个域平台返回数据格式的影响,因而只能针对性的进行处理,不一定能以标准化格式进行处理,所以本节就不把它考虑在内了。
不过,如果跨域的平台也是以这种统一的标准化的格式返回数据,那真可以偷着笑了。
下面来研究一下Ajax、表单和代理这三种传输方式的提交格式和返回格式。
1.Ajax
Ajax可以说是ExtJS最基本的数据传输方式,无论是表单的提交或是代理,都是以这为基础的。
当然,表单也可以使用传统的HTML方式来提交,而这,已不在本书讨论的范围,因而,本书所说的表单提交,都是指以Ajax方式提交的表单。
Ajax方式的数据传输,提交数据比较灵活,这个可根据不同的需要灵活处理。
而对于提交后返回的数据,则可以使用统一的数据格式,以便客户端进行处理。
2.表单
在ExtJS中,如果没有特殊设置,表单是以Ajax方式提交,而这,实际上与传统的HTML表单提交区别不大。
因而,对于接收表单的数据,还是要按习惯的方式处理。
而对于表单提交后返回的数据格式,是有特殊要求的,这主要包括两种格式。
第一种格式是字段有错误时的格式,格式如下:
{
success:
false,
errors:
{
field1:
"错误信息",
field2:
"错误信息",
...
}
}
在以上格式中,属性success的值必须是false,以表明表单的提交存在错误,而属性errors则包含了存在错误的字段以及错误信息。
格式中的field1、field2对应的就是表单中字段的配置项name的值。
第二种格式则是没有字段错误,但存在其他错误或者是提交成功后的返回格式,格式如下:
{
success:
true或false,
msg:
"信息"
}
以上格式中,属性success是必须的,true表示表单提交成功,false则表示提交失败。
属性msg则可根据项目的规定或自己的喜好进行定义,但必须与代理使用的属性相同,从而实现统一的返回格式。
3.代理
代理无论是提交还是返回的数据格式,都是有固定格式,但为了保持一定的灵活性,提交格式可由配置项进行定义,如Ext.data.writer.Json的encode配置项,可决定提交数据是以流形式提交还是以HTTP变量的形式提交。
因而,标准化提交格式主要的方式就是使用固定的配置项。
代理的返回格式基本上是固定的,主要格式如下:
{
success:
true或false,
total:
数据总数,
data:
[...],
msg:
"信息"
}
以上格式中的4个属性都可通过配置项进行修改,因而,为了实现标准化,必须固定这些配置项。
6.4.2扩展代理,以实现数据传输的标准化
从ExtJS的主要数据传输方式可以了解到,标准化格式其中一个重点就是标准化代理的提交格式和返回格式,而这,可通过扩展代理来实现。
对代理进行扩展,主要目的就是固定Reader和Writer的配置项,这样,不但实现了数据传输的标准化,还可减少重复代码,提高代码的可重用性。
在实现之前,要先了解哪些配置项是需要修改的,哪些配置项是不需要修改。
对于提交格式,主要是指Ext.data.writer.Json的配置项,包括以下这些:
❑allowSingle:
是否允许提交单个记录,默认值是true,允许提交单个记录。
如果允许提交单个记录,在服务器端,就要对提交的数据先判断提交的是对象还是数组,才能进一步对数据进行处理,这无疑增大了处理难度。
因而,应该毫不犹豫的把它设置为false,无论是单个记录还是多个记录,统一以数组的形式提交到服务器,便于服务器进行处理。
❑encode:
决定数据以什么形式提交,默认值为false,是以数据流形式提交到服务器。
因而,在服务器,需要以读取数据流(类似于读取上传文件)的方式来获取数据,这对许多开发人员来说,会非常的不习惯,也不方便,因而,要毫不犹豫的修改为true,以习惯的HTTP变量方式提交。
❑root:
设置了encode为true后,在服务器使用哪个变量名来获取数据,就需要使用该配置项来定义。
我比较习惯定义root的值为data,这样,在服务器就可以使用data来获取数据。
这个,可根据设计要求或个人喜好来定义。
❑writeAllFields:
是否只提交修改过的字段。
默认值为true,无论字段是否修改过,都会提交。
我习惯提交全部字段,因为在服务器单独提取个别修改字段,不如整体处理来得方便。
对于返回格式,主要是指Ext.data.reader.Json的配置项,包括以下这些:
❑messageProperty:
用来定义返回的数据中,可获取到响应信息的属性,也就是用来定义6.4.1节代理格式中的msg属性的配置项。
这个可根据设计要求或个人喜好设置,我个人偏好于使用msg作为属性。
❑root:
用来定义返回的数据中,可获取到记录的属性。
也就是用来定义6.4.1节代理格式中的data属性的配置项。
这个可根据设计要求或个人喜好设置,我个人偏好于使用data作为属性。
❑successProperty:
用来定义返回的数据中,可获取到数据返回是否成功的属性。
也就是用来定义6.4.1节代理格式中的success属性的配置项。
默认值就是success,一般情况下,不建议修改该属性。
❑totalProperty:
用来定义返回的数据中,可获取到记录总数的属性。
也就是用来定义6.4.1节代理格式中的total属性的配置项。
默认值就是total,一般情况下,不建议修改该属性。
从以上介绍可以了解到,需要定义的配置项主要包括encode、root、allowSingle、messageProperty。
明确了要统一的配置项之后,就可实现扩展代理(以下称为格式化代理)了。
但在实现之前,要考虑是把格式化代理定义在Ext.ux命名空间下,还是定义在SimpleCMS.ux命名空间下。
这两种命名方式的主要区别就在于目录的位置。
一般情况下,通用的扩展最好是定义在Ext.ux命名空间下,这样便于区分,但存在的问题是这些扩展会分布在ext\src\ux目录下,不便于维护,例如,在当前项目,把扩展放到ext\src\ux目录,由于要展开的目录比较深,要找出相应的类还是比较费劲的。
如果放在SimpleCMS.ux命名空间下,最大的问题就是要使用到另一个项目的时候,需要修改命名空间。
以我的经验来说,我偏好SimpleCMS.ux命名空间,主要原因就是便于维护,而这个问题的重点就是Ext本身扩展就很多,因而,在ext\src\ux目录下本身就有很多目录和文件,要在里面找项目的文件比在app\ux目录下找要费劲。
而且考虑到如果要把扩展复制到另外一个项目,直接把app\ux目录复制过去,然后修改命名空间就可以了,而不需要在ext\src\ux目录下一个个找出来再复制过去。
当然,这个问题,可通过预先把所有自己的扩展整合到一个目录,再进行复制的方式来解决,但总的来说,还是不如在app\ux目录下定义来得方便。
提示把自己的扩展放在app\ux的目录的想法来自于ExtJS4.2的示例SimpleTask,然后经过实践,感觉这样确实是方便多了,在解决方案资源管理器中找扩展要依次展开ext→src→ux目录,感觉比较烦人。
建议多点研究一下ExtJS中的示例,会获益良多的。
想法确定后,就可以在Scripts\app目录下依次添加目录ux→data→proxy,然后在proxy目录下添加添加一个名为Format.js的JavaScript文件来定义格式化代理。
这样做的目的是为了将类名定义为SimpleCMS.ux.data.proxy.Format,以符合ExtJS的类命名规则,这个可参考SimpleCMS.ux.data.proxy.Format的父类Ext.data.proxy.Ajax的命名规则,Ajax.js文件就是在data\proxy目录下。
当然,如果图方便,不按照这个命名规则命名也行。
在类名和父类都已经清楚的情况下,要写出类的定义代码也就不难了,代码如下:
Ext.define('SimpleCMS.ux.data.proxy.Format',{
extend:
'Ext.data.proxy.Ajax',
})
为了能像Ext.data.proxy.Ajax哪样,在定义代理的时候,可使用值为ajax的type配置项来指定使用Ext.data.proxy.Ajax代理,就必须定义别名,也就是定义alias配置项。
在Ext.data.proxy.Ajax中,别名的定义为“proxy.ajax”,再观察一下其他代理类的别名定义,都需要添加“proxy.”作为前缀,因而,在格式化代理的别名中添加前缀是必不可少的。
而我打算用format作为格式化代理的别名,因而,格式化代理的完整别名会是“proxy.format”。
别名定义好以后,就要统一读取器的格式了,要统一的配置项主要包括root和messageProperty,代码如下:
reader:
{
type:
'json',
root:
"data",
messageProperty:
"msg"
},
代码中,由type配置项可以知道,这里将使用JSON读取器(Ext.data.reader.Json),返回格式中,数据必须在data属性中,而信息则包含在msg属性中。
接下来,要统一的是编写器的格式,主要包括的配置项是encode、root和allowSingle,代码如下:
writer:
{
type:
"json",
encode:
true,
root:
"data",
allowSingle:
false
},
从代码可以看到,编写器将使用JSON编写器(Ext.data.writer.Json),提交的数据将以HTTP变量方式提交,提交的变量的名称是data。
无论是一个数据还是多个数据,都将以数组形式提交。
下面做一个简单的测试来测试一下这个格式化代理。
测试很简单,建立一个模型,然后进行一些操作就行了。
打开application.js文件,添加launch方法,在方法内添加以下代码:
launch:
function(){
Ext.define('Test',{
extend:
'Ext.data.Model',
fields:
['id','text'],
proxy:
{
type:
'format',
api:
{
create:
'/test/add',
read:
'/test/',
update:
'/test/update',
destroy:
'/test/delete'
}
}
});
Test.load(1,{
success:
function(rec,opts){
console.log(rec);
rec.destroy();
}
})
vara=Ext.create('Test',{text:
'test'});
a.save();
}
代码中,定义了一个带有id和text这两个字段的模型Test,模型的代理将使用刚刚定义的格式化代理。
配置项api为代理定义了执行添加、加载、修改和删除等操作时的地址。
接着,调用模型的load方法加载数据。
如果数据加载成功,则在Firebug的控制台输出数据,并调用destroy方法删除数据。
最后是创建一个新的模型实例,然后调用save方法保存它。
要代码顺利运行,还得在requires配置项中加入对格式化代理的引用,不然就会出现找不到文件的错误。
注意如果在Firebug中看到如图61所示的警告信息和错误信息,说明在定义类的时候,没在requires配置项中加入对所需类的引用。
例如,图中顶部的警告信息说明没有引用标签面板(Ext.tab.Panel),而底部的错误信息就说明找不到格式化代理,这些都是未能正确设置requires配置项造成的。
如果看到这些警告信息或错误信息,加入对所需类的引用就行了。
在本书后续的讲解中,将不再提醒大家将所需类加入到requires配置项中,大家自行处理就行了。
图61由于未引用类,在Firebug输出的警告信息和错误信息
接下来还要在Controllers目录创建一个名为TestController的空MVC控制器,在控制器内,先加入以下代码引用Json.NET:
usingNewtonsoft.Json;
usingNewtonsoft.Json.Linq;
注意在后续的讲解中,将不再提醒大家在类中加入引用。
如果VisualStudio提示找不到对象,大家根据具体情况加入相应的引用就行了。
然后将Index方法的返回值修改为JObject,再加入以下代码:
returnnewJObject(
newJProperty("success",true),
newJProperty("data",newJArray(
newJObject(
newJProperty("id",1),
newJProperty("text","test")
))
)
);
代码将根据返回格式创建一个JSON对象,对象内包含了success属性和data属性。
好了,现在在VisualStudio主菜单选择生成→重新生成解决方案来生成解决方案。
等左下角提示全部重新生成已成功后,在主菜单选择调试→开始执行(不调试)在浏览器打开应用程序。
如果这时候,Firebug还没打开,就先打开Firebug,再刷新一次页面。
当应用程序执行后,会在Firebug的控制台看到如图62所示的输出信息。
从输出信息可以看到,测试代码发送了3个请求,第一个请求是用来加载id为1的数据,第二个请求是添加记录的请求,第三个请求为删除记录的请求。
在信息中还显示了一个对象,这就是第一个请求返回后根据返回的数据创建的模型实例。
图62测试格式化代理时控制台显示的信息
在这里,关注的是数据的格式,因而,先展开第一个请求,然后切换到响应标签页,会看到服务器返回了以下的数据:
{
"success":
true,
"data":
[
{
"id":
1,
"text":
"test"
}
]
}
根据格式可以知道,这正是格式化代理要求的数据返回格式,如果修改TestController的代码,将success的值修改为false,或将属性名称data修改为其他名称,客户端就能根据返回的数据创建模型实例了。
下面展开第二、三个请求,并切换到Post标签页,会看到以下提交数据:
//第二个请求提交的数据
data[{"text":
"test"}]
//第三个请求提交的数据
data[{"id":
1,"text":
"test"}]
从提交的数据格式可以看到,提交参数data正是格式化代理中,编写器定义的root配置项的值。
再看看提交值,都是数组形式,这说明allowSingle配置项起作用了。
在服务器,就可使用统一的数据处理方式来处理数据了。
从测试结果来看,格式化代理的运作一切正常。
对于Store、模型等比较多的项目,这可减少不少定义代理时的重复代码。
当然,更大的好处是服务器可以统一处理提交数据和返回固定格式的数据。
6.4.3在服务器统一输出接口
和客户端一样,在服务器的方法内,每次返回数据的时候,都要按照返回格式写一次代码,因而存在不少重复代码。
为了减少重复代码,最好的方式当然是实现统一的输出接口。
下面来实现这个。
在解决方案添加一个名为Helper的目录,并在目录内创建一个名为ExtJS的类。
类名定义为ExtJS,可以清楚的表明这是一个用来处理ExtJS数据的类。
接下来,在类中定义一个名为WriterJObject,返回值为Jobject的静态方法,该方法将根据返回格式返回数据。
根据6.4.1节可以知道,返回格式中的属性主要包括success、errors、total、msg和data这五个属性。
在这五个属性中,除了属性success是必须的外,其他属性都是可选的。
因而,可以使用以下方式来定义WriterJObject的参数:
publicstaticJObjectWriterJObject(
boolsuccess,
JObjecterrors=null,
int?
total=null,
stringmsg=null,
objectdata=null
)
{
}
代码中,定义了默认值的参数叫做可选参数,这样,在调用方法时,就不用指定不必要的参数,而使用这些参数的默认值。
这样做的好处是,不用根据不同格式,去定义一堆参数不同的重载方法。
而且,在方法内,如果检查到参数的值是默认值null,也就不用把这参数写入返回的对象中。
由于data参数返回的数据类型可能是JArray,也可能是JObject,所以在这里将它设置为object,就能分别处理这两种数据了。
在方法内,除了必须在返回的Jobject对象中加入success属性外,其他属性,如果值为null,则不用加入JObject中,具体代码如下:
JObjectjo=newJObject(
newJProperty("success",success)
);
if(errors!
=null)
{
jo.Add(newJProperty("errors",errors));
}
if(total!
=null)
{
jo.Add(newJProperty("total",total));
}
if(msg!
=null)
{
jo.Add(newJProperty("msg",msg));
}
if(data!
=null)
{
jo.Add(newJProperty("data",data));
}
returnjo;
好了,现在来测试一下该方法。
切换到TestController控制器,将Index方法的代码修改为以下代码:
returnExtJS.WriterJObject(true,data:
newJObject(
newJProperty("id",1),
newJProperty("text","test")
));
代码中,只向WriterJObject方法传递了success和data参数,因而,在输出的时候也只会输出这两个属性,与之前代码的输出是一样的。
如果现在重新生成解决方案,并在浏览器打开应用程序,获得的结果与之前的测试是一样的。
现在,还未能充分体现出WriterJObject方法的好处,等阅读完本书后,就可体会到了。
6.4.4统一的错误处理
统一错误处理有什么好处?
好处就是,你不用在调用Ext.Ajax的request方法的时候,再去编写处理错误的代码,也不需要在调用存储的laod方法时候,为他们编写额外的错误处理代码。
总的来说,主要目的就是减少重复代码,便于维护。
要编写统一的错误处理,就要先了解ExtJS框架远程调用的回调机制。
页面处理数据交互的方式主要有Ajax、表单提交和动态加载脚本等三中方式。
在ExtJS经过封装后,基本的数据交互都是采用Ajax方式进行交互的,比较特殊的情况是上传文件时会使用表单方式提交,跨域获取数据采用动态加载脚本的方式。
因而,如果没有特殊情况,在这里就只讨论Ajax这种交互方式。
还要明确的一点是,无论是表单提交(不包括文件上传的表单),还是存储加载数据,最终的交互都是依赖Ajax来实现的,也就是说,统一错误处理要先从最基本的Ext.Ajax入手。
4.Ext.Ajax
使用Ext.Ajax对象时,会有以下3个回调函数:
success、failure和callback。
在这3个回调函数中,callback无论请求是成功还是失败都会触发;而success则在请求的状态码为200到300(不包含300)或304时才会触发;而failure则与success对应使用,不符合success的条件的请求都会触发failure。
选择callback,还是选择success和failure,可以根据自己的习惯来做。
建议的做法是使用success和failure,因为这样可以使用全局统一的错误处理机制来处理failure的结果。
那么,怎么来实现这个全局统一的错误处理机制呢?
一是可以在5.9节中创建的全局变量类中添加方法进行处理,一是创建一个扩展类来处理。
根据面向对象的封装原则,最好的方式当然是创建扩展类的方式。
下面,在Scripts\app\ux\data目录下添加一个名为FailureProcees.js的JavaScript文件,并加入以下定义代码:
Ext.define('SimpleCMS.ux