3

I created one web API application with versioning. I am going to use Microsoft.AspNet.WebApi.Versioning package to do that.

Webapi configuration:

public static class WebApiConfig
{
    public static void Register(HttpConfiguration config)
    {
        // Web API configuration and services
        config.AddApiVersioning(o => {
            o.AssumeDefaultVersionWhenUnspecified = true;
        });
        // Web API routes
        config.MapHttpAttributeRoutes();

    }
}

Here my assumption is if I didn't pass the version, by default it will select version 1.

Controller code:

[ApiVersion("1.0")]
[RoutePrefix("api/users")]
public class UserV1Controller : BaseController
{
    public UserV1Controller()
    {
    }

    [Route("all", Name = "UsersCollection")]
    public async Task<IHttpActionResult> GetAll()
    {
        var items = await UnitOfWork.Users.GetPaged(x => x.OrderBy(y => y.Id), 1, 20);
        //Add mapping to DTO 
        return Ok(items);
    }

}

If I test with http://localhost:10280/api/users/all?api-version=1.0 URL, it working fine. I am going to implement this feature in the existing project.

For backward compatibility, I tried http://localhost:10280/api/users/all URL. It gives me the following error with 500 as the status code.

{ "Message": "An error has occurred.",
"ExceptionMessage": "The index cannot be less than 0 or equal to or larger than the number of items in the collection.\r\n
Parameter name: index\r\nActual value was 0.",
"ExceptionType": "System.ArgumentOutOfRangeException",
"StackTrace": " at
System.Web.Http.WebHost.Routing.HostedHttpRouteCollection.get_Item(Int32 index)\r\n at Microsoft.Web.Http.Dispatcher.ApiVersionControllerSelector.GetControllerName(HttpRequestMessage request)\r\n at Microsoft.Web.Http.Dispatcher.ControllerSelectionContext.<>c__DisplayClass6_0.<.ctor>b__0()\r\n at System.Lazy1.CreateValue()\r\n at System.Lazy1.LazyInitValue()\r\n at System.Lazy`1.get_Value()\r\n
at Microsoft.Web.Http.Dispatcher.ConventionRouteControllerSelector.SelectController(ControllerSelectionContext context)\r\n at Microsoft.Web.Http.Dispatcher.ApiVersionControllerSelector.SelectController(HttpRequestMessage request)\r\n at System.Web.Http.Dispatcher.HttpControllerDispatcher.d__15.MoveNext()" }


Update 1:

After discussions and a bit of help, I can confirm that this default version works with conventional routing.

The issue reproduced while using attribute routing. Please check my updated code.

  1. I don't have any default API routing in the WebAPIConfig file.
  2. I update Route Prefix and Route in the controller

Now the issue reproduced.

You can also reproduce the issue with combining conventional and attribute.

Update 2:

Now I noticed that the issue lies in the configuration. If I add the routing configurations in the Owin startup class directly it working fine. 

    public partial class Startup
{
    public void Configuration(IAppBuilder app)
    {
        ConfigureAuth(app);
        var configuration = new System.Web.Http.HttpConfiguration();
        var httpServer = new System.Web.Http.HttpServer(configuration);

        // reporting api versions will return the headers "api-supported-versions" and "api-deprecated-versions"
        configuration.AddApiVersioning(options =>
        {
            options.ReportApiVersions = true;
            options.AssumeDefaultVersionWhenUnspecified = true;
        });
        configuration.MapHttpAttributeRoutes();
        app.UseWebApi(httpServer);

    }
}

It creates new HttpConfiugration other than using our existing WebAPIConfig class. So I don't know it may impact any other functionalities

So if i configure Owin to use webaPI instead of GlobalConfiguration.Configure(WebApiConfig.Register) in the Global.asax, it iw working fine.

Akhil
  • 1,918
  • 5
  • 30
  • 74
  • This is the route config for mvc. We need the route config for your webapi. this is most likely located in the WebApiConfig.cs if not changed – Marco Jan 02 '19 at 09:11
  • ok. So only I confused. Actually, I removed that default route lines as I configured only for attribute routing. I updated now. Sorry if I confused you. – Akhil Jan 02 '19 at 09:14
  • Na. It's ok. Your route config seems ok. I can now safely reproduce your error. Now let's just find a solution for it – Marco Jan 02 '19 at 09:27
  • ok, thanks. I tried all the ways specified. If not got anything seems like go ahead by extending ControllerSelector – Akhil Jan 02 '19 at 09:32
  • Is there a controller using version 2, that has the same routeprefix? e.g. `[RoutePrefix("api/users")]`? because this is causing your error in my local project. If I delete the second controller or its routeprefix, the error goes away. This is only natural, since there cannot be 2 identical routes for 2 different controllers. – Marco Jan 02 '19 at 09:47
  • Ok I understood. But here I created only one controller as a sample. – Akhil Jan 02 '19 at 09:54
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/186037/discussion-between-akhil-and-marco). – Akhil Jan 02 '19 at 10:06
  • I reproduced the behavior. Using conventional routing it is working fine. If I used Attribute routing, reproduced the issue with single controller – Akhil Jan 02 '19 at 11:58

3 Answers3

3

If you do not specify a version it is considered unversioned. In this case you have to set the AssumeDefaultVersionWhenUnspecified property, which if not told otherwise, assumes a default version of "1.0", if none is specified:

config.AddApiVersioning(o => {
    o.AssumeDefaultVersionWhenUnspecified = true;
});

This functionality is documented here: https://github.com/Microsoft/aspnet-api-versioning/wiki/Existing-Services-Quick-Start

Marco
  • 22,856
  • 9
  • 75
  • 124
  • no. I can access if i provide the api version explicitly. I also tried the same sample specified in the documentation but same error. Any other configuration? – Akhil Jan 02 '19 at 09:03
  • my fault, please edit your question with your route config. I can recreate your error by adding the `[Route]` decorator on an action method. – Marco Jan 02 '19 at 09:03
  • Your route config is still missing from your question – Marco Jan 02 '19 at 09:08
  • I added the route.config file code. As this is API only I think we don't care about route.config. correct? I just care only in webApiConfig file – Akhil Jan 02 '19 at 09:11
  • Now I updated with my latest findings. But I don't know why it happened like that – Akhil Jan 03 '19 at 09:27
2

This is a confirmed bug that only occurs when hosting on a vanilla IIS implementation (e.g. non-OWIN). Despite the HttpRouteCollection having an indexer, the HostedHttpRouteCollection defined by System.Web does not support it. Instead of throwing NotSupportedException, a generic ArgumentOutOfRangeException is thrown.

For more details see: https://github.com/Microsoft/aspnet-api-versioning/issues/428.

The fix is in and is now available in package version 3.0.1.

Chris Martinez
  • 3,185
  • 12
  • 28
0

You can try setting up the default version number and configure middleware to use it when version number is not specified.

Reference: Scott's blog on routing

services.AddApiVersioning(
    o =>
    {
        o.AssumeDefaultVersionWhenUnspecified = true );
        o.DefaultApiVersion = new ApiVersion("1.0");
    } );
Manoj Choudhari
  • 5,277
  • 2
  • 26
  • 37
  • That won't work, since his routes do not use DateTimes as version attributes. A suitable overload would have been `new ApiVersion(1,0)` – Marco Jan 02 '19 at 12:26
  • thanks. but that is not the issue. I think this is related to routing. This issue reproduced when I use RoutePrefix and Route attributes only. I mean Attribute routing – Akhil Jan 03 '19 at 02:29