This issue is driving me nuts.
I have a get method that takes ID array with custom ModelBinder (I can call http://xckvjl.com/api/results/1,23,34,)
I want to introduce Gets on actions. (so that I can call as http://alskjdfasl.com/api/results/latest)
I have the following web api routing.
config.Routes.MapHttpRoute("DefaultApi", "{controller}/{id}", new { id = RouteParameter.Optional });
config.Routes.MapHttpRoute("ApiWithAction", "{controller}/{action}");
I have tried with (Please note here that I am using my custom model binder)
config.Routes.MapHttpRoute("DefaultApi", "{controller}/{id}", new { id = RouteParameter.Optional }, new {id = @"\d+" });
You can reproduce this error with this sample:
public class TestController: ApiController {
[HttpGet]
public virtual IHttpActionResult Get([ModelBinder(typeof(CommaDelimitedCollectionModelBinder))]IEnumerable<int> id = null )
{ }
[HttpGet]
public virtual IHttpActionResult Latest( )
{ }
}
public class CommaDelimitedCollectionModelBinder : IModelBinder
{
public bool BindModel(HttpActionContext actionContext,
ModelBindingContext bindingContext)
{
var key = bindingContext.ModelName;
var val = bindingContext.ValueProvider.GetValue(key);
if (val == null)
{
return false;
}
var s = val.AttemptedValue;
if (s != null && s.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).Length > 0)
{
var array = s.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).Select( n=>Convert.ToInt32(n)).ToArray();
Type type = bindingContext.ModelType.GetGenericArguments().First();
var typeValue = Array.CreateInstance(type, array.Length);
array.CopyTo(typeValue, 0);
bindingContext.Model = array;
}
else
{
bindingContext.Model = new[] { s };
}
return true;
}
}
If I write as:
[HttpGet]
[Route("Tests/latest")]
public virtual IHttpActionResult Latest( )
{ }
It works. However, I want global level routing. Otherwise for every action I will have to write the same.
Please advise.
So here's the dilimma:
With this route definition:
when you call
http://alskjdfasl.com/api/results/latest
,latest
becomes the value ofid
, which obviously will throw error since string cannot be converted toint
array.Adding this route definition
won't help since now you either have to deal with order or define routes for your other actions which are now being masked due to this.
Without declaring routes at individual level, the only other options are to make the templates different or create your own route constraint that understands arrays.
I would give these a shot first:
If nothing works, I would say go with attribute routes. They are cleaner.