Frage

I've implemented a throttling mechanism on my Login-action following this post. It works the way I want it to, except one thing. Whenever the mechanism is returning my message, I get redirected to a new, blank view with my message in it.

Is it possible, and if so how, to return this message back to my login-controller / view so it can be displayed in my _LoginPage.cshtml?

Here's my attribute:

[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
public class ThrottleAttribute : ActionFilterAttribute
{
    public string Name { get; set; }
    public int Seconds { get; set; }
    public string Message { get; set; }
    public int AllowedRetries { get; set; }
    private int _loginAttempts = 1;

    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        string key = string.Concat(Name, "-", filterContext.HttpContext.Request.UserHostAddress);
        bool allowExecute = false;
        _loginAttempts++;

        while (_loginAttempts <= AllowedRetries)
        {
            return;
        }

        if (HttpRuntime.Cache[key] == null)
        {
            HttpRuntime.Cache.Add(key,
                true, 
                null, 
                DateTime.Now.AddSeconds(Seconds),
                Cache.NoSlidingExpiration,
                CacheItemPriority.Low,
                null); 

            allowExecute = true;
        }

        if (!allowExecute)
        {
            if (String.IsNullOrEmpty(Message))
            {
                Message = "AllowedRetries Exceeded. You have to wait {n} seconds.";
            }

            filterContext.Result = new ContentResult
            {
                // TODO: Redirect message text to login-view
                Content = Message.Replace("{n}" , Seconds.ToString())
            }; 

            filterContext.HttpContext.Response.StatusCode = (int)HttpStatusCode.Conflict;
        }
    }
}

LoginController:

[Throttle(Name = "LoginThrottle", Seconds = 10, AllowedRetries = 3)]
[HttpPost]
public ActionResult Index(LoginViewModel model)
{
    ...login logic...

    return View(model);
}

UPDATE

Following @lin's suggestion I got the information I wanted, but I'm not able to display it in my view. In my cshtml file I start by setting a variable before referencing it further down in my markup:

@{
    var informationToUser = ViewBag.Information ?? "";
}
<div class="panel panel-primary>
    ...
    <p>@informationToUser</p>
</div>

Still, nothing happens :( As mentioned in comments below, my url now looks like this: http://localhost:54508/?information=AllowedRetries%20Exceeded.%20You%20have%20to%20wait%2010%20seconds.

War es hilfreich?

Lösung

I'm not sure this is what you want. But it should work. See below code:

Throttle Attribute

  if (!allowExecute)
    {
        if (String.IsNullOrEmpty(Message))
        {     
            Message = "AllowedRetries Exceeded. You have to wait {n} seconds.";
        }
        filterContext.HttpContext.Response.StatusCode = (int)HttpStatusCode.Conflict;

        var values = new RouteValueDictionary(new
        {
                action = "Login",
                controller = "Account",
                exceptiontext = Message.Replace("{n}", Seconds.ToString())
        });
        filterContext.Result = new RedirectToRouteResult(values);
    }

Login Action Method

  [HttpGet]
  public ActionResult Login(string returnUrl, string exceptiontext)
    {
        ViewBag.ReturnUrl = returnUrl;
        ViewBag.Exceptiontext = exceptiontext;
        return View();
    }

Update: You don't need to validate if the ViewBag is null.

Login View

 //
<div class="panel panel-primary>
    <p>@ViewBag.Exceptiontext </p>
</div>
//

The URL should like :

http://localhost:58929/Account/Login?exceptiontext=AllowedRetries%20Exceeded.%20You%20have%20to%20wait%2010%20seconds.

Andere Tipps

I developed an OSS project called MvcFlash that does exactly what you want to do and handles a lot of what you want. You can install it through NuGet and it is easy to get started with.

PM > Install-Package MvcFlash.Web

Using it in code is as simple as this.

// In your attribute
Flash.Error("Ooops", "There was an exception", id: "Exception")

In your view:

@Html.Flash()

The reason you need the id in the attribute is because the attribute might execute multiple times and thus you would see the message multiple times when you call flash. Setting the id makes the message unique.

Here is some more information.

https://github.com/khalidabuhakmeh/MvcFlash2

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top