ASP.NET Web Api ignores RouteParameter.Optional when selecting the action
asp.net-mvc, asp.net-mvc-4, asp.net-web-api, c#
Solution
You may well have an argument this feels like an awkward solution to something relatively straight forward, but I think the root cause is because you are trying to roll both 'GetAll' and 'GetOneById' into the same method.
You can leave the route config out of the problem if you are willing to split out the methods in the controller. IMHO this is not such a bad thing; it complies with the separation of concerns principle. It also does not mean any duplication of code as you can centralise the logic to access the object(s) you want to return.
These controller methods should give you the routes you want without having to adjust the route config:
[HttpGet]
public IEnumerable<string> GetAll()
{
return GetStrings(new int[]{});
}
[HttpGet]
public string GetOneById(int id)
{
return GetStrings(new int[]{id}).FirstOrDefault();
}
private IEnumerable<string> GetStrings(int[] ids)
{
return // fetch strings using int array
}
Problem
I found this issue by the following steps: Create a new WebApi project with the installed project template Go to Controllers/ValuesController.cs, there are two Get methods like this: `public IEnumerable<string> Get()` // this one provides GetAll function `public string Get(int id)` // this one is GetOneById I don't like this design because I think the two api methods can be combined into one: `public IEnumerable<string> Get(string ids)` when ids is null, it returns all the records, otherwise returns the results by ids(which looks like id1,id2,id3...) I modified the route as well: ``` config.Routes.MapHttpRoute( name: "DefaultApi", routeTemplate: "api/{controller}/{ids}", defaults: new { ids = RouteParameter.Optional } ); ``` Now I think everything is ready. However when I visit `/values` in the browser(without specifying the ids parameter), I was told "no action was found", until I add a default value for `ids`: ``` public IEnumerable<string> Get(string ids = null) ``` From now on everything works as I expected. Still I can't move the `defaults` parameter in the route. Which means: the code declares the ids parameter as optional twice. Looking into the source code of MVC4 located at `System.Web.Http.Controllers.ActionSelectorCacheItem.FindActionUsingRouteAndQueryParameters` method, I can see the optional parameters defined in the route are ignored when finding the right action. Personally I think it's really bad, even the workaround is quite easy. Or I misunderstood the relations between routes and action methods? I consider routes are rules used to help a request to find its corresponding action, while "a parameter is optional" is exactly part of a rule.