更好的方法是限制我的MVC控制器中的表单验证尝试次数,并在达到限制时重定向到另一个操作?

时间:2011-04-05 13:14:56

标签: asp.net-mvc-3

我对MVC很新,正在寻找一些急需的指导。

目标是确定用户何时针对任何给定操作敲打墙壁,并将其重定向到包含“帮助台”联系信息的其他页面。

我的代码功能齐全但丑陋且不太可重用。 (验证在ViewModel中处理):

    [HttpPost]
    public ActionResult MyForm(MyForm model)
    {....

        if (ModelState.IsValid){..... return RedirectToAction("Success")}

        var attempts = Convert.ToInt32(TempData["Attempts"]);            
        if (attempts >= 3)
        {
            TempData["LocalMessages"] = new Message
            {
                Class = "Alert",
                Text =
                    "It looks like you are having trouble ...Please Contact...."
            };
            return RedirectToAction("HelpDesk");
        }
        attempts += 1;
        TempData["Attempts"] = attempts;
        return View(model);

理想的解决方案是利用动作过滤器等​​功能,以便我可以针对任何给定的操作执行以下操作:

    [HttpPost, MaximumAttemptBeforeRedirect(4, "HelpDesk")]
    public ActionResult MyForm(MyForm model)

非常感谢任何帮助。

2 个答案:

答案 0 :(得分:1)

您可以将尝试次数包括为视图模型的属性:

[HttpPost]
public ActionResult MyForm(MyForm model)
{
    if (ModelState.IsValid) 
    {
        return RedirectToAction("Success");
    }

    ModelState.Remove("Attempts");
    model.Attempts++;
    if (model.Attempts >= 3)
    {
        TempData["LocalMessages"] = new Message
        {
            Class = "Alert",
            Text = "It looks like you are having trouble ...Please Contact...."
        };
        return RedirectToAction("HelpDesk");
    }
    return View(model);
}

并在表单中包含尝试作为隐藏字段的次数:

<%= Html.HiddenFor(x => x.Attempts) %>

答案 1 :(得分:1)

我找到了我想要的东西(我在博客文章http://bradwilson.typepad.com/blog/2011/02/advanced-aspnet-mvc-3-presentation.html中使用了Brian Wilson的Stateful Storage课程):

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class MaximumAttemptBeforeRedirectAttribute : FilterAttribute, IActionFilter
{        
    readonly IStatefulStorage _storage = StatefulStorage.PerSession;        
    private readonly int _maxAttempt;
    private readonly RedirectToRouteResult _newRoute;
    private string _keyName;
    public MaximumAttemptBeforeRedirectAttribute(int attempts, string controller, string action)
    {
        _maxAttempt = attempts;
        var rvDic = new RouteValueDictionary {{"action", action}, {"controller", controller}};
        _newRoute = new RedirectToRouteResult(rvDic);

    }

    Attempts GetCount(string name)
    {
        return _storage.GetOrAdd(name, () => new Attempts());
    }        

    public void OnActionExecuted(ActionExecutedContext filterContext)
    {
        _keyName = string.Format("{0}{1}Attempts",
                      filterContext.RouteData.Values["controller"],
                      filterContext.RouteData.Values["action"]
                     );

        var errCount = filterContext.Controller.ViewData.ModelState.Values.Where(v => v.Errors.Count != 0).Count();
        if (errCount > 0) //If errors, increment attempt count by 1
        {
            GetCount(_keyName).Increment();
        }
        else //If no errors, reset attempt count.
        {
            GetCount(_keyName).Reset();
        }


        if (GetCount(_keyName).Number < _maxAttempt) return; //user is within allowed threshold for validation attempts so stop here.

        //Do we give the user the opportunity to navigate back to the page and try again in durring life of current session?
        GetCount(_keyName).Reset(); //user can now go back to the page again and try another x times.

        var route = string.Format("{0}/{1}", filterContext.RouteData.Values["controller"],
                                  filterContext.RouteData.Values["action"]);

        Logger.WarnFormat("Max Attempts were reached for {0}/{1}", filterContext.RouteData.Values["controller"], filterContext.RouteData.Values["action"]);                        
        filterContext.Controller.TempData["LocalMessages"] = new Message
                                                    {
                                                        Class = "Alert", 
                                                        Text = "You have exceeded the allowed number of attempts for " + route
                                                    };
        filterContext.Result = _newRoute;
    }

}

这允许我将以下内容添加到我的ActionResult或我的控制器:

[HttpPost, MaximumAttemptBeforeRedirect(4, "MyController", "SomeAction")]