为啥不像 'IsPostBack' 那样实现 'IsRefresh'、'IsBackPost' 呢?
Posted
技术标签:
【中文标题】为啥不像 \'IsPostBack\' 那样实现 \'IsRefresh\'、\'IsBackPost\' 呢?【英文标题】:Why not implement 'IsRefresh', 'IsBackPost' just like is 'IsPostBack'?为什么不像 'IsPostBack' 那样实现 'IsRefresh'、'IsBackPost' 呢? 【发布时间】:2011-01-17 07:14:06 【问题描述】:我可以看到,我自己和很多人都在 ASP.NET 中遇到这两个项目的问题......刷新按钮,后退按钮......(我看到很多人问如何禁用这两个按钮在浏览器中..)
现在在Page中实现另外两个布尔变量(IsRefresh,IsPostBack)有什么问题...如果可以规避和实现这些问题,这对开发人员来说将是一笔巨大的财富...
当您回答时,如果您还可以在您的网络应用程序中包含您正在采取的步骤,以避免在这种情况下重新发布(有时在数据库中)会有所帮助。
谢谢
【问题讨论】:
相关:***.com/questions/1791416/… 【参考方案1】:这不是很清楚。服务器识别“回发”的唯一方法是根据您的请求传递给页面的标头。当您发布表单时,“IsPostBack”为真,如您所料。当您刷新页面或按返回时,会发送完全相同的请求数据。如果不更改发送请求的浏览器的行为,应用程序无法检测到用户发出了非标准请求(返回或刷新)。
如您所知,大多数浏览器都会向您指示“单击返回将重新发送表单数据...”,但这只是浏览器发出的警告,让您知道它将发送完全相同的数据再次请求。服务器不知道这一点,也没有(本机)方法来解释信息。
双重回发预防技巧
防止数据被两次发布的一种方法是确保每个 PostBack 都包含一些您可以验证的唯一数据。这个过程相当简单。
在每次页面加载时,您都需要为该页面的 PostBack 事件创建一个唯一标识符。唯一 id 是什么并不重要,只要它在顺序页面加载时不一样。将该唯一标识符放在页面上的隐藏字段和用户会话(或 cookie)中。然后,在每个 PostBack 验证隐藏字段中的 cookie 匹配会话中的值。如果值匹配,则 PostBack 是可以相应处理的页面的原始帖子。执行必要的操作后,您将需要再次更改两个位置的唯一标识符。这样,如果用户回击并选择“重新发送数据”,隐藏字段将与会话密钥不匹配,您可以丢弃重复的帖子数据。
【讨论】:
我想(读作绝望)知道,在这种情况下我应该采取什么步骤,以避免在数据库中执行另一个调用/命令......一个经典的例子是用户输入 cmets 的页面...点击刷新再次插入评论,并且很难检查是否已经输入了相同的评论,因为评论很长,并且有可能有效地输入了相同的评论。 @The King:很简单。将评论添加到数据库后,您向同一页面发出 Response.Redirect。然后当用户刷新或返回时不会再次添加评论。【参考方案2】:实现这两个额外的布尔属性的问题是,确实没有(可靠的)方法来区分 Back / Refresh 触发的请求。当点击这些按钮时,浏览器会:
-
如果允许,则显示缓存中的页面,或者
再次执行完全相同的请求(可能要求用户先确认)
您正在遇到案例 #2。当第二个请求发生时,服务器将收到与原始请求相同的完全相同的请求数据。因此,ASP.NET(和大多数其他框架)将像处理原始请求一样处理请求。有几种方法可以解决这个问题:
-
更改缓存策略以允许浏览器重新显示先前请求的结果(可能会解决 Back 问题,但不能解决 Refresh 问题。
添加一个隐藏字段(或
ViewState
中的数据),该字段包含预期回发的页面上的唯一值。然后添加一些数据结构以保留“已使用”值的列表,以及在执行任何关键操作之前测试重复项的逻辑。
使用Post/Redirect/Get pattern 确保POST
请求永远不会出现在浏览器历史记录中。
解决方案 #3 非常简单,几乎适用于所有情况。基本上,与其返回结果/确认作为对“关键”回发的回复,不如重定向到新的 URL:
protected void btnCritical_Click(object sender, EventArgs e)
DoSomethingThatShouldNotBeDoneTwice();
Response.Redirect("confirmation.aspx");
【讨论】:
【参考方案3】:aspnet_isapi 识别来自表单内容的回发,特别是 ViewState 字段。它没有内在的方法来区分回发和刷新。
过去我已经看到了一些解决此问题的策略。一种是为每个请求分配一个 guid,并将其用作您正在写入的表中的唯一键或索引。
您可以创建一个所有页面都继承自的基本页面。
public partial class PageBase : System.Web.UI.Page
private Guid _requestId;
protected Guid RequestId
get
return _requestId;
protected virtual void Page_Load(object sender, EventArgs e)
if (!IsPostBack)
_requestId = Guid.NewGuid();
ViewState.Add("requestId", _requestId);
else
_requestId = (Guid)ViewState["requestId"];
public partial class _Default : PageBase
protected override void Page_Load(object sender, EventArgs e)
base.Page_Load(sender, e);
// now do stuff
【讨论】:
是的,您可以使用它来实现 jorns 建议跟踪“已使用”请求。以上是关于为啥不像 'IsPostBack' 那样实现 'IsRefresh'、'IsBackPost' 呢?的主要内容,如果未能解决你的问题,请参考以下文章
为啥 flexbox 不像 div 中的其他元素那样居中我的图像?