找这篇文章的原因是因为在使用百度编辑器的时候发现使用linkbutton不能将form里面的参数传回后台,导致后台获取不了编辑器的内容,结果一直报错。后来使用了button和imagebutton就正常了,但是看了下这三种button的基类发现button和linkbutton继承的基类完全是一样的,那么问题就在linkbutton和button两个类本身上了,于是提出疑问:linkbutton与button的不同地方在哪里?在网上搜索了一下发现这篇文章,然后焕然大悟。之前之所以用linkbutton获取不到编辑器是因为linkbutton使用的postback机制,并没有将form中的数据回发给后台,而button和imagebutton则是使用submit方式form数据提交到后台进行处理,当然button野可以使用postback机制,具体看下文,文章原处:
由UseSubmitBehavior 引发的“血案”
前段时间碰到button控件的一个属性问题UseSubmitBehavior。
在MSDN上查看,UseSubmitBehavior属性时,有这样一句话
“获取或设置一个布尔值,该值指示 Button控件使用客户端浏览器的提交机制还是 ASP.NET回发机制。 ”
" 默认情况下,此属性的值为true,从而导致Button控件使用浏览器的提交机制。如果指定为false,则 ASP.NET 页框架将客户端脚本添加到页面,以将窗体发送到服务器。"
这里讲到客户端浏览器的提交机制和 ASP.NET回发机制,这两种机制有些不明白。于是在论坛上发了个帖子,只有一个人简单的回复了下。讲的内容也只是先前我再msdn上看的介绍UseSubmitBehavior的内容。
可能是问题没有问清楚,于是google了下。发现了一篇文章内容如下:
现在就分段进行分析下:
页面回传与js调用服务端事件、PostBack的原理
第一章、Asp.net中服务端控件事件是如何触发的
Asp.net 中在客户端触发服务器端事件分为两种情况:
一.WebControls中的Button和HtmlControls中的Type为submit的HtmlInputButton
这两种按钮最终到客户端的表现形式为:<inputtype="submit"value="Submit">,这是Form表单的提交按钮,点击以后会作为参数发送到服务端,参数是这样的:
控件的name属性=控件的value值,对应上面的例子就是:Submit1= Submit。服务器端会根据接收到的控件的name属性的这个key来得知是这个按钮被点击了,从而在服务端触发这个按钮的点击事件。
析:这里讲到两种服务器控件<asp:button/>和<asp:imagebutton/>,他们默认触发事件的方式就如同前面MSDN讲到的浏览器的提交机制。我们可以做个测试查看下,到底是怎么回事。这个很常见,往往最习以为常的东西却是最让人忽视的。
例1:
前台
<asp:Button ID="Button4" runat="server" Text="Button" οnclick="Button4_Click" />
<asp:ImageButton ID="ImageButton1" runat="server"
οnclick="ImageButton1_Click" />后台:
protected void ImageButton1_Click(object sender, ImageClickEventArgs e)
{
response.write("1");
}
protected void Button4_Click(object sender, EventArgs e)
{
response.write("2");
}在第一次请求页面以后查看页面源代码,
发现没有什么可以引发事件触发的代码,代码如下:
<div>
<input type="hidden" name="__VIEWSTATE" id="__VIEWSTATE" value="/wEPDwUJMzg1OTE4NTYzZBgBBR5fX0NvbnRyb2xzUmVxdWlyZVBvc3RCYWNrS2V5X18WAQUMSW1hZ2VCdXR0b24xkOp6AfsL58gS89iqJEbq4u4u1ec=" />
</div>
<div>
<input type="hidden" name="__EVENTVALIDATION" id="__EVENTVALIDATION" value="/wEWAwKCp4bMAgKF2fXbAwLSwpnTCHJGAELeUt/C0D8Kcm4sB62EZr5U" />
</div>
<div>
<input type="submit" name="Button4" value="Button" id="Button4" />
<input type="image" name="ImageButton1" id="ImageButton1" src="" style="border-width:0px;" /> </div>
但是当我们点击button和imagebutton的时候,事件方法会触发,输出1,或2。我想这就是客户端浏览器的提交机制。
二.HtmlControls中的Type为button的HtmlInputButton和其它所有的控件事件,比如LinkButton点击,TextBox的Change事件等等:
这些事件在客户端产生后会经过一个统一的机制发送到服务端。
1.首先asp.net页框架会使用两个Hidden域来存放表示是哪个控件触发的事件,以及事件的参数:
<!―表示触发事件的控件,一般是这个控件的name -->
<inputtype="hidden"value=""/>
<!―表示触发事件的参数,一般是当某个控件有两个以上的事件时,用来区别是哪个事件-->
<inputtype="hidden"value=""/>
2.服务端会生成一个jscript的方法来处理所有这些事件的发送,这段代码是:
<scriptlanguage="javascript"type="text/javascript">
function __doPostBack(eventTarget, eventArgument)
{
var theform = document.WebForm2;
theform.__EVENTTARGET.value = eventTarget;
theform.__EVENTARGUMENT.value = eventArgument;
theform.submit();
}
</script>
3.每个会引发服务端事件的控件都会在响应的客户端事件中调用上面的代码:
比如,HtmlControls中的Type为button的HtmlInputButton的点击事件
<!―客户端的点击事件调用__doPostBack,eventTarget参数为'Button2',表示是name为'Button2’控件触发的事件,eventArgument为空,表示这个Type为button的HtmlInputButton只有一个客户端触发的服务端事件-->
<input language="javascript" Button2','')" type="button" value="Button" />
又比如,TextBox控件的Change事件
<!―客户端的onchange事件调用__doPostBack,eventTarget参数为’TextBox1’,表示是name为’TextBox1’控件触发的事件,而TextBox控件只有一个客户端触发的服务端事件TextChanged,故服务器就会去触发这个TextBox的TextChanged事件->
<input type="text" TextBox1','')" language="javascript" />
4.客户端触发事件后调用__doPostBack方法,将表示触发的控件源的eventTarget和事件参数eventArgument分别付给两个隐藏域__EVENTTARGET和__EVENTARGUMENT,然后提交Form,在服务端根据__EVENTTARGET和__EVENTARGUMENT来判断是哪个控件的什么事件触发了。
析:以上4个描述讲的大概意思就是,除了第一讲的服务器控件以外,其他的事件的触发都是通过__doPostBack(eventTarget, eventArgument)来解决的。然后我从工具箱里托了一些控件出来,第一个放的是<asp:LinkButton/>,运行查看源代码发现了
function __doPostBack(eventTarget, eventArgument){....}内容存在.。觉得很正确。又拖放了个<asp:TextBox/>控件,运行查看源代码发现并没有__doPostBack(){}方法,心里就有点怀疑上面的说法。接着拖放了<asp:DropdownList/>,<asp:CheckBox/>,等等几个服务器控件,并没有发现__doPostBack()。是不是事件没有加,于是在<asp:TextBox/>加了TextChanged()事件,发现还是老样子。别的几个控件也对应的加上了对应的事件。可也是同样的结果。调试进去后发现,而且事件方法也并没有触发。又开始怀疑是不是上面的错了,最后想了想服务器控件想要回发,不是还要设置AutoPostBack ="true"嘛,于是将此属性添加上去,果然看到了<asp:LinkButton/>同样的效果,接着我又把对应的事件方法又去掉,只留下AutoPostBack ="true"。__doPostBack()依然存在。原来autoposback="true"才是关键.那为什么<asp:LinkButton/>为什么就不用设置Autopostpack属性,发现<asp:LinkButton/>根本就没有这个属性。(嘿嘿,为什么没有呢,·····有待调查)。回头想想__doPostBack(){}方法和Autopostpack属性两个从名字上就是相关联的嘛!
第二章、PostBack的原理
__doPostBack是一个纯粹并且是非常简单的javascript函数,大部分的页面PostBack都是由它触发的。注意,这里是“大部分”,因为只有两个Web Server Control 会自己触发页面的PostBack,其它的所有控件都是通过__doPostBack函数触发页面的PostBack,那先来看一下这个函数的定义吧:
CODE1:
<input type="hidden" value="" />
<input type="hidden" value="" />
function __doPostBack(eventTarget, eventArgument) {
if (!theForm.onsubmit || (theForm.onsubmit() != false)) {
theForm.__EVENTTARGET.value = eventTarget;
theForm.__EVENTARGUMENT.value = eventArgument;
theForm.submit();
}
}
通过上面的代码可以看到,__doPostBack带有两个参数,eventTarget是标识将要引发页面PostBack的控件ID,eventArgument参数提供了在引发页面PostBack事件时所带的额外参数。当然这个函数被函数时,这两个参数的值将赋值给页面的两个隐含变量__EVENTTARGET和__EVENTARGUMENT,然后调用页面的submit方法提交页面表单。这就是为什么我们可以通过Request.Form[“__EVENTTARGET”]获取得到引发页面PostBack的控件ID的原因。
了解了__doPostBack函数后,我们可以很容易的利用它非常方便地自己触发自定义的PostBack事件。那上面也说了,大部分的控件都是调用
析:Request.Form[“__EVENTTARGET”]方法可以查看到触发该事件的控件编号(应该是name才对)(除了button和imagebutton这两种服务器控件,下面会有介绍)。
第三章Button PostBack做法
引了页面的PostBack,只有两个控件是例外,Button和ImageButton,正是因为它们不是通过调用__doPostBack来回发事件,所以通过表单隐含变量__EVENTTARGET和__EVENTARGUMENT是无法获取得到引发PostBack的Button或ImageButton的ID和参数值的,可通过下面的方式实现
1)在页面中加如LinkButton,页面就会在页面中加载POSTBACK所需的JS
<input type="hidden" value="" />
<input type="hidden" value="" />
function __doPostBack(eventTarget, eventArgument) {
if (!theForm.onsubmit || (theForm.onsubmit() != false)) {
theForm.__EVENTTARGET.value = eventTarget;
theForm.__EVENTARGUMENT.value = eventArgument;
theForm.submit();
}
}
2)利用GetPostBackEventReference给客户端生成__doPostBack()
如:比如前台页面
<asp:Buttonid="Button1"runat="server"Text="Button"></asp:Button>
(1)
<ahref="#"onclick="document.getElementById('Button1').click()">触发服务器端按钮事件</a>
(2)
利用GetPostBackEventReference给客户端生成__doPostBack()
前台<ahref="#"onclick="<%=PostBack()%>">触发服务器端按钮事件</a>
后台
protectedstringPostBack()
{
returnthis.Page.GetPostBackEventReference(this.Button1,"haha");
}通过__EVENTARGUMENT="haha"可以判断是不是点了那个链接的PostBack把Button1的按钮事件这么写:
if(Request["__EVENTARGUMENT" ]=="haha")
{
Response.Write("这个是链接的PostBack");
}
else
{
Response.Write("这个不是链接的PostBack");
}析:上述主要告诉我们,<asp:button/>和<asp:imagebutton/>是不能通过Request.Form[“__EVENTTARGET”]方法获取到触发控件的编号的。作者告诉了一种验证的方法。但是觉得不太好,自己想了下,之前查过的 UseSubmitBehavior刚好验证这里。
总结上面的,再结合到MSDN其实我们可以得到,button服务器控件是特殊的,它除了通过浏览器来触发意外,还可以通过function __doPostBack(eventTarget, eventArgument){....}这个来实现。那怎么让这个js的方法出来呢,就是呼应了前面的UseSubmitBehavior=“false”属性。
例2:
前台:
<asp:Button ID="Button4" runat="server" Text="Button" οnclick="Button4_Click" UseSubmitBehavior="false"/>
后台:
protected void Button4_Click(object sender, EventArgs e)
{
response.write("ddd");
}执行以上代码,查看源文件__doPostBack()方法存在,点击按钮输出ddd,事件方法也执行了。到此两种触发机制了解了。
Bug:
问题:『使用 __doPostBack会导致』
回发或回调参数无效。在配置中使用<pages enableEventValidation="true"/>或在页面中使用<%@ Page EnableEventValidation="true" %>启用了事件验证。出于安全目的,此功能验证回发或回调事件的参数是否来源于最初呈现这些事件的服务器控件。如果数据有效并且是预期的,则使用ClientScriptManager.RegisterForEventValidation方法来注册回发或回调数据以进行验证。
问题分析及解决方案:『来源网络』
这个要具体分析。本来这个措施是 asp.net2.0用来防止客户端 “ 欺诈 ” 服务器端的。例如本来输出到客户端的一个事件被触发时需要回发的命令是 “__doPostback('ctl01$abc','user_1')”的,如果采取手段把回发参数由 user_1改为 user_5 了,服务器端会重新核对输出的是不是 user_5,发现和这个页面上一个输出的脚本不一致,就会产生这个异常。
但是,很多程序员写的程序按照过去的习惯(或者按照更加高级灵活的设计例如一些 Ajax组件)没有考虑这个问题或者是忽略这个欺诈的可能性,写的程序可能会修改参数或者修改目标控件。
因此这样具体问题具体分析。不太可能跟浏览器距离服务器的远近有关,应该还是编程逻辑问题。你应该对出异常的画面以及所使用的数据进行分析。有时候,经常也需要将这个参数设置为 false,放弃安全管理
以上的内容是对button控件的UseSubmitBehavior属性的了解,其实在之前,还有一个问题也是纠缠不清。就是js中return false;的问题。我们结合到UseSubmitBehavior 看看。会有什么效果。
例3:
<asp:Button ID="Button4" runat="server" Text="Button" οnclick="Button4_Click" UseSubmitBehavior="false" OnClientClick="alert('hello');"/>
后台:
protected void Button4_Click(object sender, EventArgs e)
{
response.write("ddd");
}
与例2相比多了OnClientClick="alert('hello');;执行后查询源代码
<input type="button" name="Button4" value="Button" οnclick="alert('hello');__doPostBack('Button4','')" id="Button4" />
发现__doPostback()js方法在,前面多了句alert('hello');
首先先弹出了hello,(客户端事件),然后输出了ddd(服务器段事件,只不过由客户端触发的).再看下面的例子,
例4:
<asp:Button ID="Button4" runat="server" Text="Button" οnclick="Button4_Click" UseSubmitBehavior="false" OnClientClick="alert('hello');return false;"/>
后台:
protected void Button4_Click(object sender, EventArgs e)
{
response.write("ddd");
}
与例3相比alert('hello');return false;多了句return false;这也是我们讨论的关键。
执行,查看源代码
<input type="button" name="Button4" value="Button" οnclick="alert('hello');return false;__doPostBack('Button4','')" id="Button4" />
__doPostBack('Button4','')
依然在,前面多了句return false;在看看功能上发现弹出了hello,但是没有输出ddd.这就是我们想要的。