0

With the routeTemplate defined as "api/{controller}/{id}" and having below methods in my controller (ValuesController)

    [HttpGet]
    public IEnumerable<string> GetX()
    {
        return new string[] { "xx1", "xx2" };
    }

    [HttpGet]
    public IEnumerable<string> GetY([FromUri]Customer c)
    {
        return new string[] { "c1", "c2" };
    }

     public class Customer
     {
        public bool IsMarried { get; set; }
        public string CustName { get; set; }
     }

Using the url - "/api/values?IsMarried=false&CustName=aa" results in error - "Multiple actions were found that match the request..." . I am not able to fix above issue without changing the routeTemplate to something like "api/{controller}/{action}/{id}". If anyone knows how to fix above without changing the routeTemplate kindly suggest.

2
  • You could use the [Route("...")] directive to define a different path for a specific controller only. Commented Dec 18, 2016 at 21:48
  • Thank you @TasosK. for your reply. Looks like adding "Route" attribute may not be helpful in my specific case as the client which i am using can only call REST urls (ie. without using action names) for some reasons. Commented Dec 18, 2016 at 22:28

3 Answers 3

1

Solution one: Routing

[RoutePrefix("api")]
public class FooController : ApiController
{
    [Route("Foo/getall")]
    [HttpGet]
    public IEnumerable<string> GetX()
    {
        return new string[] { "xx1", "xx2" };
    }

    [Route("foo/search")]
    [HttpGet]
    public IEnumerable<string> GetY([FromUri]Customer c)
    {
        return new string[] { "c1", "c2" };
    }

    public class Customer
    {
        public bool IsMarried { get; set; }
        public string CustName { get; set; }
    }
}

Remember to add attribute route mapping:

configuration.MapHttpAttributeRoutes();

Solution two: Optional parameters

[RoutePrefix("api")]
public class FooController : ApiController
{
    [HttpGet]
    public IEnumerable<string> GetY([FromUri]Customer c)
    {
        if(c == null)
           // no parameters where set.

        if(c.IsMarried != null)
           // do stuff
        // etc....
        return new string[] { "c1", "c2" };
    }

    public class Customer
    {
        public bool? IsMarried { get; set; }
        public string CustName { get; set; }
    }
}
Sign up to request clarification or add additional context in comments.

4 Comments

Thank you for answering. Yes those two approaches should work. For approach in the client application i am using may not be able to provide specific route name in the url, ie. it can only call "api/Values?...". Basically i was trying to ask why i am getting "Multiple actions found ..." error can't webApi map to correct action based on the query string ? looks like i am missing something here,
@SunilThakur - No, you are not missing anything. For any 1 controller you can route based on action names (using the Route attribute) or (by default) on the Http Action verb (get, put, post, etc). You cannot route based on the parameters, it is not like c# with method overloads based on parameter types.
I can have methods in same controller such that - Get(), Get(string x), Get(string x, string y) etc - and web-api calls respective method based on the parameters provided in query string ie. "api/Values" will invoke Get(), "api/Values?x=2" will invoke Get(string x) etc. It works fine if only simple types are specified in the action method parameters. It's only when we have complex type (in Get method) as parameter it breaks.
So basically like @Petre Turcu describes.
1

If you cant specify action names then you have two options.

Action based on Parameter

Create an overload that takes a default (null for reference type) parameter. Depending on the state o the parameter do the approriate action. As your sample is very abstract I have no idea if this would work for you or not.

[HttpGet]
public IEnumerable<string> GetY([FromUri]Customer c = default(Customer))
{
    if(c == null /* or some other state */)
        return new string[] { "xx1", "xx2" };
    return new string[] { "c1", "c2" };
}

New Controller

Create a new Controller and define your additional action there. If the actions are very different in nature this might be a better solution. No code sample here is needed, just create a new controller and move the existing method.

1 Comment

Thank you for answering. Yes agreed these approaches should work. My concern was why web-api can not map to correct action method in the scenario specified in the question, looks like i am missing something regarding how web-api maps to action methods.
1

Use simple types and map them in the controller:

[HttpGet]
public IEnumerable<string> GetY(bool IsMarried, string CustName)
{
    var cust = new Customer {IsMarried = IsMarried, CustName = CustName};
    return new string[] { "c1", "c2" };
}

Note that you will lose functionalities like ModelState.IsValid so you would need to invoke the validators manually.

Take a look at Action Selection section to see how it works for simple vs complex params.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.