4

The following attribute is used to restrict use of an action to an ajax request:

public class AjaxRequestAttribute : ActionMethodSelectorAttribute
{
    public override bool IsValidForRequest(ControllerContext controllerContext, System.Reflection.MethodInfo methodInfo)
    {
        return controllerContext.HttpContext.Request.IsAjaxRequest();
    }
}

I have the following controller action methods defined:

[AjaxRequest]
public ActionResult Login()
{
     ...
}

[HttpPost, AjaxRequest]
public ActionResult Login(LoginModel model, string returnUrl)
{
    ...
}

The following error occurs when the ajax post is made:

The current request for action 'Login' on controller type 'AgentController' is ambiguous between the following action methods: System.Web.Mvc.ActionResult Login() on type NappWebsiteMvc.Controllers.AgentController System.Web.Mvc.ActionResult Login(NappWebsiteMvc.Models.Agent.LoginModel, System.String) on type NappWebsiteMvc.Controllers.AgentController

It seems that the HttpPost attribute is ignored when using the additional attribute. If I remove the AjaxRequest attribute from the two methods, then the code works.

What should be the correct implementation? Thanks!

Stafford Williams
  • 9,696
  • 8
  • 50
  • 101
gxclarke
  • 1,953
  • 3
  • 21
  • 42

3 Answers3

2

As requested by Pawel, here is the code:

public class AjaxGetAttribute : ActionMethodSelectorAttribute
{
    public override bool IsValidForRequest(ControllerContext controllerContext, MethodInfo methodInfo)
    {
        var isHttpGet = new AcceptVerbsAttribute(HttpVerbs.Get).IsValidForRequest(controllerContext, methodInfo);
        return isHttpGet && controllerContext.HttpContext.Request.IsAjaxRequest();
    }
}

public class AjaxPostAttribute : ActionMethodSelectorAttribute
{
    public override bool IsValidForRequest(ControllerContext controllerContext, MethodInfo methodInfo)
    {
        var isHttpPost = new AcceptVerbsAttribute(HttpVerbs.Post).IsValidForRequest(controllerContext, methodInfo);
        return isHttpPost && controllerContext.HttpContext.Request.IsAjaxRequest();
    }
}
gxclarke
  • 1,953
  • 3
  • 21
  • 42
1

I believe that you can also include the HttpGet attribute on the first action method:

[HttpGet, AjaxRequest]
public ActionResult Login()
{
     ...
}

[HttpPost, AjaxRequest]
public ActionResult Login(LoginModel model, string returnUrl)
{
    ...
}

It seems that all ActionMethodSelector attributes must return true for the action method to be valid. Making this change means that the first method will not be used for POSTs, even if it is an Ajax request.

My scenario is slightly different, but this solution worked for me.

Ben Mills
  • 27,454
  • 14
  • 42
  • 38
0

It's because HttpPost also extends the ActionMethodSelectorAttribute, with its own IsValidForRequest override. Have you tried separating your attribute into AjaxRequestAttribute and AjaxPostRequestAttribute?

public class AjaxPostRequestAttribute : HttpPostAttribute
{
    public override bool IsValidForRequest(
        ControllerContext controllerContext, MethodInfo methodInfo)
    {
        var isHttpPost = base.IsValidForRequest(controllerContext, methodInfo);
        return isHttpPost 
            && controllerContext.HttpContext.Request.IsAjaxRequest();
    }
}

You would then just decorate like so:

[AjaxPostRequest]
public ActionResult Login(LoginModel model, string returnUrl)
{
    ...
}

Update

I am sorry, I did not notice the sealed keyword on the HttpPostAttribute source.

danludwig
  • 46,965
  • 25
  • 159
  • 237
  • 1
    Thanks for your response. So, the method selection pipeline will only consider the first ActionMethodSelectAttribute that it finds? It seems odd that it would behave this way. I would think that match failure would only occur when an attribute returns false, otherwise the remaining attributes are processed. I can't find any documentation to support either approach, but this would be my expectation for a typical "pipeline" behavior. – gxclarke Jan 06 '12 at 19:09
  • 1
    Separating the attribute into AjaxRequestAttribute and AjaxPostRequestAttribute does solve the problem. HttpPostAttribute and HttpGetAttribute are sealed, so the implementation was a bit different than what you posted. Thanks for your help. I'm just surprised that it the method selection pipeline step behaves this way. – gxclarke Jan 06 '12 at 19:25