ASP.NET Web窗体技术虽然很慢,但肯定已经成为过去。 它已被具有Angular 6和类似堆栈的Web API所取代。 但是我继承了Web Forms上的一个项目,并拥有巨大的遗产。 我有一些朋友正负相似的情况。 需要开发和维护的有关旧技术的长篇应用程序。 Web窗体可以在PostBack上不刷新整个页面,而仅刷新其中的一部分。 UpdatePanel中包装了什么。 这增加了交互性,但是它仍然运行缓慢并且消耗大量流量,因为 每次在服务器上进行渲染时,完成的标记就会传递给客户端,客户端需要插入该标记而不是当前的div。 顺便说一下,UpdatePanel只是在div中呈现,然后替换标记。
如何减少流量?
- 在页面上编写WebMethod并使用AJAX工具从客户端调用它,当您收到响应时,通过JS更改DOM。
该解决方案的缺点是您无法在控件中定义WebMethod。 我不想在页面上编写所有功能,尤其是在不同页面上多次使用该功能时。 - 编写一个asmx服务,然后从客户端调用它。 这样比较好,但是在这种情况下,控件和服务之间没有明确的连接。 服务数量将随着控件数量的增加而增加。 此外,ViewState将对我们不可用,这意味着我们将在访问服务时显式传递参数,因此我们将进行服务器验证并检查用户是否有权执行他所请求的操作。
- 使用ICallbackEventHandler接口。 我认为这是一个很好的选择。
我将更详细地介绍它。
要做的第一件事是从ICallbackEventHandler继承我们的UserControl并编写RaiseCallbackEvent和GetCallbackResult方法。 奇怪的是,有两个,第一个负责从客户端接收参数,第二个负责返回结果。
它看起来像这样
public partial class SomeControl : UserControl, ICallbackEventHandler { #region /// <summary> /// /// </summary> private Guid _someFileId; #endregion #region ICallbackEventHandler /// <inheritdoc /> public void RaiseCallbackEvent(string eventArgument) { // try { dynamic args = JsonConvert.DeserializeObject<dynamic>(eventArgument); _someFileId = (Guid) args.SomeFileId; string type = (string) args.Type; } catch (Exception exc) { // throw; } } /// <inheritdoc /> public string GetCallbackResult() { // try { // - return JsonConvert.SerializeObject(new { Action = actionName, FileId = _someFileId, }); } catch (Exception exc) { // throw; } } #endregion }
这是服务器端。 现在的客户
var SomeControl = { _successCallbackHandler: function (responseData) { let data = JSON.parse(responseData); switch (data.Action) { case "continue":
这还不是全部。 我们仍然需要生成JS来连接所有功能。
protected override void OnLoad(EventArgs e) { base.OnLoad(e);
这显然是控件的代码。
最有趣的是使用GetCallbackEventReference方法生成JS函数。
我们传递给它
- 链接到我们的控件
- JS变量的名称,其值将通过eventArgument在RaiseCallbackEvent方法中传递给服务器(上面的行在JSON中序列化对象以进行传输,并实际上设置了此args变量的值)
- 成功的JS函数回调名称
- 执行上下文(我不使用它)
- 发生错误时JS回调函数的名称
- 我们将使用ASP.NET工具验证到达服务器的请求
所有这些将如何协同工作?
从JS我们可以调用SomeControl.CallServer,该函数将创建一个局部变量args并将控制权传递给一个函数,该函数将通过AJAX向服务器发出请求。
接下来,将控制传递给RaiseCallbackEvent服务器方法。 现在,args客户端变量中的所有内容都落入服务器输入参数eventArgument中。
执行RaiseCallbackEvent后,控制权将传递给GetCallbackResult。
我们将通过返回返回的字符串将发送到客户端,并进入SomeControl._successCallbackHandler函数的输入参数,即responseData中。
如果在某个阶段服务器代码抛出异常,则控制权将被转移到客户端SomeControl._failCallbackHandler
关于ViewState仍然需要说。 ViewState从客户端传输到服务器,可以使用,但只能在ReadOnly模式下使用,例如 ViewState不会发送回客户端。
乍一看,该设计令人困惑,但是如果您看一下,它看起来非常方便,并且节省了流量。
我想在本文中讨论的第二个问题是可单击的div或如何从客户端调用UpdatePanel更新。
为什么需要可点击的div,您可以只使用<asp:Button>吗?
我喜欢div可以根据需要组成,我不受输入类型=“ button”的限制
为了实现,有必要从IPostBackEventHandler接口继承
他只有一种方法
public void RaisePostBackEvent(string eventArgument)
现在,与前面的情况一样,我们需要生成JS来调用此方法
看起来像这样
Page.ClientScript.GetPostBackEventReference(this, callbackArgument)
在服务器上设置了callbackArgument,并且无法在客户端上对其进行更改。 但是,您始终可以将某些内容放入HiddenField中。 我们有完整的PostBack
现在,GetPostBackEventReference的结果可以挂在任何div或span或任何其他对象的onclick上。
或者只是通过计时器从JS调用。
确保将控件注册为异步控件(在OnLoad上我们称为
ScriptManager.GetCurrent(Page)?.RegisterAsyncPostBackControl(this);
),否则,即使在UpdatePanel中,也会调用同步的PostBack并更新整个页面,而不仅仅是UpdatePanel的内容
例如,使用上述2种方法,我实现了这种情况。
用户单击该按钮,向服务器发送了一个长时间操作(10-15秒)的小请求,用户收到了简短响应,在分析过程中,客户端脚本调用了setTimeout。 在setTimeout中,传递了一个函数以回调到服务器,以了解先前请求的操作的结果。 如果结果准备就绪,请在UpdatePanel中调用PostBack-更新指定的UpdatePanel。 如果结果尚未准备好,请再次调用setTimeout。
祝所有仍在使用Web Forms的人都好运,我希望本文能使您的系统更快,更漂亮,并且用户将感谢您。