The current request for action {0} on controller t

2020-07-11 06:03发布

问题:

I have two actions and I want my routes /users and /users/{id} to be different. However it throws me error.

Is it possible implement this sorta thing without manually creating every route, I will have other controllers that will follow similar pattern and writing custom routes for all of them seems redundant and bad idea in general.

Error

The current request for action 'Index' on controller type 'UsersController' is ambiguous between the following action methods: System.Web.Mvc.ActionResult Index() on type Api.Controllers.UsersController System.Web.Mvc.ActionResult Index(Int32) on type Api.Controllers.UsersController

Code

public class UsersController : Controller
{
    public ActionResult Index()
    {
        return null;
    }

    public ActionResult Index(int id)
    {
        return null;
    }
}

回答1:

You need an ActionMethodSelector:

public class RequiresParameterAttribute : ActionMethodSelectorAttribute {

   readonly string parameterName;

   public RequiresParameterAttribute(string parameterName) {
      this.parameterName = parameterName;
   }

   public override bool IsValidForRequest(ControllerContext controllerContext, MethodInfo methodInfo) {
      return controllerContext.RouteData.Values[parameterName] != null;
   }
}

And the controller:

public class UsersController : Controller
{
    public ActionResult Index()
    {
        return null;
    }

    [RequiresParameter("id")]
    public ActionResult Index(int id)
    {
        return null;
    }
}

I'm not sure if the above will work, but should give you an idea.



回答2:

You can also try something like this:

public class UsersController : Controller
{   
    public ActionResult Index(int? id)
    {
        if(id == null) {
            // case A
        }
        else {
            // case B
        }
    }
}


回答3:

Max Toro's answer was useful, although I needed to specify more parameters.

I changed the selector attribute to this:

public class RequiresParameterAttribute : ActionMethodSelectorAttribute
{

    readonly string[] parameterName;

    public RequiresParameterAttribute(string[] parameterName)
    {
        this.parameterName = parameterName;
    }

    public override bool IsValidForRequest(ControllerContext controllerContext, MethodInfo methodInfo)
    {

        //somehow, in my version of MVC controllerContext.RouteData.Values was not returning querystring parameters at all...
        var queryString = controllerContext.RequestContext.HttpContext.Request.QueryString; 

        foreach (string param in parameterName)
        {
            if (queryString.GetValues(param) == null)
                return false;
        }

        return true;
    }
}

And then it can be used as such:

[RequiresParameter(new string[] { "id", "id2", "id3" })]
public ActionResult Index(int id, int id2, int id3)
{
    //....
}


回答4:

Put [HttpGet] for the first method!

public class UsersController : Controller
{
    [HttpGet] 
    public ActionResult Index()
    {
        return null;
    }

    public ActionResult Index(int id)
    {
        return null;
    }
}


回答5:

I'd just delete the first method that takes no parameters and make the integer both optional in the routing and a nullable int in the controller. Then you can decide what to do based on the integer being null in a single action method.

You can define the action as part of the route:

/{action}/{id}

And make action required but id optional... that would allow for mapping of action based on URL. Could also specify {controller}.