2011-06-01 335 views
2

我有一个剃须刀视图,呈现一个HTML表单,它发布到服务器。 如果表单值是正确的,它会被保存到数据库。 插入后,我重定向到另一个用户可以进行进一步更改的视图。如何防止ASP.NET MVC 3中的重复表单提交?

现在用户可以点击浏览器后退按钮并重新提交表单以在db中创建另一个记录。

如何防止在我的MVC应用程序中重复提交?

回答

5

解决方法之一是在窗体加载时随机生成的窗体上放置一个隐藏的“标记”字段。当你看到这个令牌回到创建阶段时,它暂时存储在某个地方(例如,如果你使用会话的话会话)。如果您再次看到相同的表单,则可以假设同一表单已快速提交两次。

+1

这可能要求您将记录与记录一起存储并检查重复项,或将记录存储在会话中,其中重复提交只能在同一个会话中捕获。但是否则是一个很好的解 – Jay 2011-06-01 21:45:35

+0

视图由模型(类)驱动。这是如何做的?我可以在控制器中生成一些随机字符串,并分配给模型的某个字段,并存储在会话中。在保存期间,我可以从模型中拉出并与会话中的值进行比较。这是怎么做到的?如果没有,你是否有示例代码(非常高的水平)? – kheya 2011-06-01 22:18:25

+0

你如何生成该令牌?我很乐意使用会话或cookie。 – kheya 2011-06-01 22:59:27

2

创建一个cookie来代表该特定页面成功时。如果使用cookie(浏览器现在会发送每个请求)重播它,则不知道是否允许进行新的尝试。

+0

你在那个cookie中存储什么?我需要一个检测和防止重复表单提交的解决方案 – kheya 2011-06-01 22:19:47

+1

只需要页面名称或操作名称或任何内容来描述该操作。只要找到该cookie,然后在发布(Request.Cookies [“无论”])并设置成功后保存使用Response.Cookies.Add – 2011-06-02 00:34:53

2

处理发布请求后,将用户重定向到另一个HttpGet操作。 因此,当用户刷新浏览器时,后动作不会再被调用。

return RedirectToAction(“YourActionMethod”);

+1

你将如何处理浏览器后退按钮单击? – Dementic 2015-11-07 10:59:50

2

虽然客户端验证是可能的,但它不够安全。 我不知道如果此方法适用于MVC 3,但我所做的是实现一个ActionFilterAttribute

这里是实现:

public class PreventFrequentCallsAttribute : ActionFilterAttribute 
{ 
    public int DelayRequest = 5; 

    public override void OnActionExecuting(ActionExecutingContext filterContext) 
    { 
      var request = filterContext.HttpContext.Request;  
      var cache = filterContext.HttpContext.Cache; 

      var originationInfo = request.ServerVariables["HTTP_X_FORWARDED_FOR"] ?? request.UserHostAddress; 
      originationInfo += request.UserAgent; 
      var targetInfo = request.RawUrl + request.QueryString; 
      var hashValue = string.Join("", MD5.Create().ComputeHash(Encoding.ASCII.GetBytes(originationInfo + targetInfo)).Select(s => s.ToString("x2"))); 

      if (cache[hashValue] != null) 
      {  
       filterContext.Controller.ViewData.ModelState.AddModelError("ExcessiveRequests", "Excessive Request Attempts Detected.");      
      } 
      else 
      { 
       cache.Add(hashValue, originationInfo, null, DateTime.Now.AddSeconds(DelayRequest), Cache.NoSlidingExpiration, CacheItemPriority.Default, null); 
      } 

      base.OnActionExecuting(filterContext); 
    } 
} 

后,在目标控制器,只需添加该属性:

[PreventFrequentCalls(3)] 
    public PartialViewResult LogOn(LogOnViewModel model)