“和Postback说再见”上篇在此 作者提供的1.1中代码,和服务端生成的ScritpClient脚本,在此下载。 [续]和Postback说再见
为了将此新特新运用在ASP.NET1.1中,我们来挖掘一下,在.NET2.0中到底是如何实现的。
以下是跟踪过程:用birdsome介绍的方法点击more info前打开在下一步按Alt->v->u->b然后点击more info:
<
button
id="btn" onclick="javascript:WebForm_DoCallback('__Page',document.all['DropDownList1'].value,UpdateInfo,null,null,false)">More Info</button>
从此处可以知道button已经绑定上了由GetCallbackEventReference产生的脚本。在这个版本中并没有这个脚本的名字还是叫WebForm_DoCallback,Never Mind!继续,F11……
<
script
src="/ExperienceAspx2.0/WebResource.axd?a=s&r=WebForms.js&t=632430413780000000" type="text/javascript"></script>
我们还发现在页面中系统加上了上面这段脚本。WebResource.axd是一个在ASP.NET中新的内置于HTTP的处理中。用它把脚本程序包含到页面中。这个handler确保把所有的控件或者页面的Callback引用脚本包含进来。并且,webResource.axd确保所有的WebForm_DoCallback和WebForm_InitCallback成功的被执行。
下面我们就揭开它的真面目:
这个版本的WebResource.axd中包含的函数有:
function WebForm_PostBackOptions(eventTarget, eventArgument, validation, validationGroup, actionUrl, trackFocus, clientSubmit)
function WebForm_DoPostBackWithOptions(options)
function WebForm_DoCallback(eventTarget, eventArgument, eventCallback, context, errorCallback, useAsync)
function WebForm_CallbackComplete()
function WebForm_InitCallback()
function WebForm_ReEnableControls()
function WebForm_ReDisableControls()
function WebForm_FireDefaultButton(event, target)
function WebForm_GetScrollX()
function WebForm_GetScrollY()
function WebForm_SaveScrollPositionSubmit()
function WebForm_SaveScrollPositionOnSubmit()
function WebForm_RestoreScrollPosition()
function WebForm_TextBoxKeyHandler()
我们只研究其中的WebForm_DoCallback,和WebForm_CallbackComplete,其他完整的代码我将在提供打包下载。
在原文中作者给出了两个不要直接调用WebForm_oCallback的理由,第一是可以通过Client Script Mangner来管理用其GetCallbackEventReference方法来实现客户端的脚本,第二由于,WebForm_DoCallback不是Page API的所以在以后的版本中不能保证它不改名字,并且,如果其有新的修改也不会有对外公布的必要。
从该代码中可以发现它不仅适用于ie,对其他浏览器也提供了支持。它使用了一个COM对象从特定的URL中来接受发送数据。
然后在HTTP传送数据时是选择POST,还是GET是取决于数据的大小。如果大于2K的数据则采用POST。HTTP请求包含3个基本元素:__CALLBACKID,__CALLBACKPARAM,和发送的数据包。__CALLBACKID中包含目标地址,而__CALLBACKPARAM则是携带者服务段方法需要的参数。发送的数据包氏通过WebFrom_InitCallback方法来连接并且附加HTTP命令中。以上代码详细呈现了该实现过程。
如何使用Script Callback IN ASP.NET 1.1
到目前为止,我想我们把它搬到1.1中来使用也不是非常困难的事情。下面根据我的实践结果来讨论如何在Script Callback。
首先介绍我第一次错误的使用方法。
根据2.0的思路,第一感觉就是我把它的那些系统生成的脚本,我自己给他挖掘出来,添加到客户端,然后在后台添加自己的GetCallbackReference(),并实现RaiseCallback()两个方法,不就是一个1.1版本的Script Callback。
首先我根据vs2005的简单的msdn,以及跟踪其返回值,写了个假的GetCallbackReference();代码如下:
本来想对其6个参数作详细介绍的,可是考虑到这不是正确的方法,所以在此就不在叙述,如果想知道的朋友可以查看msdn2.library.com(这里的还是旧版本),或者查看vs2005的对象浏览器。
类似2.0中在Page_Load作如下引用:
String callbackRef = this.GetCallbackEventReference(this,"document.all['DropDownList1'].value","MoreInfo",null,null,false);
btn.Attributes["onclick"] = String.Format("javascript:{0}",callbackRef);
并将我提供的的所有2.0中的系统脚本加到客户端中,可是调试的时候确发现,我手工添加的脚本和服务端添加的脚本存在的区别就在于编译的顺序不一样,当编译客户端的脚本的时候服务端的脚本还没有生成,也就是说,它无法调用theForm,postUrl等变量,于是我想到,是不是可以在后台在Page_Init的时候以编程的方式加入到客户端,必须确保它是在服务端脚本加入后而执行的,至于放在Page_Init中还是在Page_PreRender()我不是很清楚。
好错误的方法讨论到此,下面介绍作者的方法,也是正确的方法,起初我想用2.0中原有的脚本也许更强大,更完善。作者的方法很简单。简单的实现了xmlHTTP的客户端与服务端的通信,但是不支持其它版本的浏览器。
后台ScriptCallback.aspx.cs:
在1.1中的效果如下:
至此我们实现了在ASP.NET1.1中的无postback的和服务端的提交获取数据。原理就是利用了xmlHTTP。不过我们还是更期待在2.0中来使用该功能。
思考与问题
整个研究学习过程中,我逃避了两点。第一,对2.0中的脚本程序没有做细致的分析,它到底是如何运行的,第二,如果我一定要用服务端的脚本,我如何通过后台把脚本以程序方式加入到客户端,并且在呈现客户端页面后再调用。如果你有兴趣的话,可以和我一起研究哦。
个人觉得,在该文章上发布的时候已近有人提出来这样做的安全性,我觉得很有道理,如此所有的通信就相当于明码在这样传来传去,对于传一些比较重要的数据的时候这样做是很危险的,我也不是很清楚2.0中有没有对这个问题提供其它可靠的有安全保障的方法。但如果把这个方法用在简单的更具客户的不同要求而时时地对页面中某些元素进行验证,少量的数据绑定,这可以算是一个不错的解决方法!如果有大量数据要通过此方法和服务端通信,是不是可以加一个loading bar,让更新数据部分的元素停止操作,而用户仍然可以操作其它控件这样的二次开发,我还没有尝试,我想不久就会尝试。所以做gon功能简单,要把功能做出漂亮的UI就很难,如果要在漂亮的UI上加上user experience就是难上加难啊!
作者信息
在此注册blog一直没有发表任何文章,我一直觉得自己水平还不够,这次下了决心,参考msdn的文章,然后加上自己总结实践,凑成了这篇文章,希望能给各位有所赏,有所析,有所用。小弟在此献丑了,我想我以后主要还是这种方式来提交和大家一起学习进步。这样我自己也能有很大收获,也能给大家带来一点内容。
关于我自己,我现在就读杭州电子科技大学的通信工程学院,2005年就是大三下了,不知道这儿大学生多么?希望能和各位交个朋友。
生活博客:http://spaces.msn.com/members/rbmnetatmsn/
常用Email:rbmxxx at hotmail.com
Connect me!
function
MoreInfo1() {
var
selectedID
=
document.all[
"
DropDownList1
"
].value;
var
xml
=
DoCallback(
"
ScriptCallback.aspx
"
,selectedID);
var
o
=
xml.responseText.split(
"
,
"
); e_ID.innerHTML
=
o[
0
]; e_UserName.innerHTML
=
o[
1
]; e_Password.innerHTML
=
o[
2
]; e_Authority.innerHTML
=
o[
3
]; e_BlogName.innerHTML
=
o[
4
]; e_Email.innerHTML
=
o[
5
]; }
function
DoCallback(url,params) {
var
pageUrl
=
url
+
"
?callback=true¶m=
"
+
params;
var
xmlRequest
=
new
ActiveXObject(
"
Microsoft.XMLHTTP
"
); xmlRequest.open(
"
POST
"
,pageUrl,
false
); xmlRequest.setRequestHeader(
"
Content-Type
"
,
"
application/x-www-form-urlencoded
"
); xmlRequest.send(
null
);
return
xmlRequest; }
其中e_ID,e_UserName等类似分别是我呈现表格的第二列的cell的ID。
protected
string
GetCallbackEventReference(System.Web.UI.Control control,
string
argument,
string
clientCallback,
string
context,
string
clientErrorCallback,
bool
useAsync)
{ string[] ReturnValue = new string[6]; if(control.ID != null) ReturnValue[0] = control.ID.ToString(); else ReturnValue[0] = "'__Page'"; ReturnValue[1] = argument; ReturnValue[2] = clientCallback; ReturnValue[3] = context!=null?context:"null"; ReturnValue[4] = clientErrorCallback!=null?clientErrorCallback:"null"; ReturnValue[5] = "false"; return String.Format("WebForm_DoCallback({0})",String.Join(",",ReturnValue)); }
var
__callbackObject
=
new
Object();
function
WebForm_DoCallback(eventTarget, eventArgument, eventCallback, context, errorCallback, useAsync) { re
=
new
RegExp(
"
//x2B
"
,
"
g
"
);
var
postData
=
__theFormPostData
+
"
__CALLBACKID=
"
+
eventTarget
+
"
&__CALLBACKPARAM=
"
+
escape(eventArgument).replace(re,
"
%2B
"
);
if
(__nonMSDOMBrowser) {
var
xmlRequest
=
new
XMLHttpRequest();
if
(pageUrl.indexOf(
"
?
"
)
!=
-
1
) { xmlRequest.open(
"
GET
"
, pageUrl
+
"
&
"
+
postData,
false
); }
else
{ xmlRequest.open(
"
GET
"
, pageUrl
+
"
?
"
+
postData,
false
); } xmlRequest.setRequestHeader(
"
Content-Type
"
,
"
application/x-www-form-urlencoded
"
); xmlRequest.send(
null
); response
=
xmlRequest.responseText;
if
(response.charAt(
0
)
==
"
s
"
) {
if
(eventCallback
!=
null
) { eventCallback(response.substring(
1
), context); } }
else
{
if
(errorCallback
!=
null
) { errorCallback(response.substring(
1
), context); } } }
else
{
var
xmlRequest
=
new
ActiveXObject(
"
Microsoft.XMLHTTP
"
); xmlRequest.onreadystatechange
=
WebForm_CallbackComplete; __callbackObject.xmlRequest
=
xmlRequest; __callbackObject.eventCallback
=
eventCallback; __callbackObject.context
=
context; __callbackObject.errorCallback
=
errorCallback;
var
usePost
=
false
;
if
(pageUrl.length
+
postData.length
+
1
>
2067
) { usePost
=
true
; }
if
(usePost) { xmlRequest.open(
"
POST
"
, pageUrl, useAsync); xmlRequest.setRequestHeader(
"
Content-Type
"
,
"
application/x-www-form-urlencoded
"
); xmlRequest.send(postData); }
else
{
if
(pageUrl.indexOf(
"
?
"
)
!=
-
1
) { xmlRequest.open(
"
GET
"
, pageUrl
+
"
&
"
+
postData, useAsync); }
else
{ xmlRequest.open(
"
GET
"
, pageUrl
+
"
?
"
+
postData, useAsync); } xmlRequest.setRequestHeader(
"
Content-Type
"
,
"
application/x-www-form-urlencoded
"
); xmlRequest.send(); } } }
可见在该版本中比作者原文的WebForm_DoCallback有了很大的改进。