1

I have a controller with two actions which have the same name, but one accepts some parameters. To disambiguate them, one accepts only GET requests, while the other accepts only POST requests. I also have an HttpAjaxAttribute which is used to enforce only Ajax calls on an action method. For some reason this solution is not reliable, sometimes on a GET request to the Import action MVC stubbornly tries to select the POST/AJAX one and throws the Ajax exception from HttpAjaxAttribute. I found a question that may be related. I thought that having the attributes attached in a particular order (HttpGet or HttpPost and then HttpAjax) would solve the problem, but it doesn't. My website worked for some time and now it fails. I have encountered this problem on seemingly random times. How to I fix it for good?

Controller actions

[HttpGet]
public ActionResult Import()
 {
     // some code
 }

[HttpPost]
[HttpAjax]
public ActionResult Import(string country, string state, string city, ImportModel[] locations)
{
    // some code
}

HttpAjaxAttribute

/// <summary>
/// Makes the controller action that has this attribute applied accept only Ajax requests.
/// </summary>
public class HttpAjaxAttribute : ActionMethodSelectorAttribute
{
    public override bool IsValidForRequest(ControllerContext controllerContext, System.Reflection.MethodInfo methodInfo)
    {
        if (!controllerContext.HttpContext.Request.IsAjaxRequest())
        {
            throw new Exception("The action " + methodInfo.Name + " can only be called via an Ajax request");
        }
        return true;
    }
}
Community
  • 1
  • 1
Pawel Krakowiak
  • 9,940
  • 3
  • 37
  • 55

2 Answers2

3

I'm pretty sure you should not throw exception from your HttpAjaxAttribute , but just return false when the action cannot serve current request.

/// <summary>
/// Makes the controller action that has this attribute applied accept only Ajax requests.
/// </summary>
public class HttpAjaxAttribute : ActionMethodSelectorAttribute
{
    public override bool IsValidForRequest(ControllerContext controllerContext, System.Reflection.MethodInfo methodInfo)
    {
        return controllerContext.HttpContext.Request.IsAjaxRequest();
    }
}

MVC will try to inspect all of the actions before it finds the right one, there's nothing stubborn in that. You should just tell framework, is action valid for current request, or not. Finally, MVC will reach HttpGet action and select it. But by throwing exception before that, you forcibly stop this process.

archil
  • 39,013
  • 7
  • 65
  • 82
  • D'oh! This will teach me to thoughtlessly steal code from the web. ;) You are absolutely right, I don't need an exception there. – Pawel Krakowiak Feb 23 '12 at 12:39
1

When you add [HttpAjax] attribute you are limiting your action method, or entire controller with what it can do.

When it comes to graceful degradation, you want to check if it's an AJAX request, if it is, then return partial view, or JSON or whatever it is you want to return. Otherwise, you'll have to return a entire view.

Because of this I suggest you don't implement HttpAjax attribute, but check in your action method whether it's a AjaxRequest:

public ActionResult Foo()
{
   if(HttpContext.Request.IsAjaxRequest())
   {
       // Return partial
   }

   // Degrade gracefully

}