Search code examples
asp.net-mvcasp.net-mvc-3controller-actions

ASP.NET MVC selects the wrong controller action


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;
    }
}

Solution

  • 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.