I have a controller like this:
namespace WebApplication1.Controllers.V1
{
[ApiVersion("1")]
[RoutePrefix("api/v{version:apiVersion}/values")]
public class ValuesController : ApiController
{
// GET api/values
public virtual IEnumerable<string> Get()
{
return new string[] { "value1 v1", "value2 v1" };
}
// GET api/values/5
public virtual string Get(int id)
{
return $"value{id} v1";
}
}
}
and another controller that inherits from V1.ValuesController like this:
namespace WebApplication1.Controllers.V2
{
[ApiVersion("2")]
[RoutePrefix("api/v{version:apiVersion}/values")]
public class ValuesController : V1.ValuesController
{
// GET api/values
public override IEnumerable<string> Get()
{
return new string[] { "value1 v2", "value2 v2" };
}
}
}
but when I make a request to http://localhost:51909/api/v2/values/5 , I got this response:
{
"error": {
"code": "UnsupportedApiVersion",
"message": "The HTTP resource that matches the request URI 'http://localhost:51909/api/v2/values/5' does not support the API version '2'.",
"innerError": {
"message": "No route providing a controller name with API version '2' was found to match request URI 'http://localhost:51909/api/v2/values/5'."
}
}
}
Is there any whay to do this? I mean inherit code from a base controller to avoid duplicated code.
Based on your setup, I don't conceptually see a reason why it wouldn't work. I suspect it may have something to do with your routing configuration. Are you using attribute or convention-based routing? Hopefully, you're not using both. I strongly discourage both; it's confusing. In addition, after many failed attempts, I've concluded that there is no real way to support API versioning for a single service using both routing styles. You can only mix styles if you use the same style for all API versions of a service that your controllers implement.
I mention that because I only see [RoutePrefix]. The route prefix does not define a route. That means your example has no routes. If it works or ever worked, it must be due to convention-based routing. You should be able to prove that by ensuring the following:
At that point, you _should_ see that no routes work with or without API versioning. To fix things, you should be able to do one of the following:
[RoutePrefix] to [Route][Route] to each actionWith all of that in place, I expect that things should be working for you, including the inheritance bits.
Change [RoutePrefix] to [Route]
Both the V1 and V2 controllers mush have [Route("api/v{version:apiVersion}/values")] ?
I believe not because the RouteAttribute is inherited.
I tried your recomendations, this is how the controllers and the WebApiConfig look like:
namespace WebApplication1.Controllers.V1
{
[ApiVersion("1")]
[RoutePrefix("api/v{version:apiVersion}/values")]
public class ValuesController : ApiController
{
// GET api/values
public virtual IEnumerable<string> Get()
{
return new string[] { "value1 v1", "value2 v1" };
}
// GET api/values/5
public virtual string Get(int id)
{
return $"value{id} v1";
}
}
}
namespace WebApplication1.Controllers.V2
{
[ApiVersion("2")]
public class ValuesController : V1.ValuesController
{
// GET api/values
public override IEnumerable<string> Get()
{
return new string[] { "value1 v2", "value2 v2" };
}
}
}
public static void Register(HttpConfiguration config)
{
// Web API configuration and services
// Web API routes
var constraintResolver = new DefaultInlineConstraintResolver()
{
ConstraintMap =
{
["apiVersion"] = typeof( ApiVersionRouteConstraint )
}
};
config.MapHttpAttributeRoutes(constraintResolver);
config.AddApiVersioning(options =>
{
options.ReportApiVersions = true;
});
}
but I'm getting the same response UnsupportedApiVersion calling /api/v2/values/5
If I change [RoutePrefix] to [Route] I get the error on both the V1 and V2 controller.
I found the issue. It's within ASP.NET Web API itself. Despite the [Route] and [RoutePrefix] attributes being inheritable, the platform intentionally ignores them. Have a look at the DefaultDirectRouteProvider.cs file and you'll see that inherited attributes are not considered. The framework implements IDirectRouteFactory through attributes. We all commonly call it _attribute routing_, but the name of the feature is actually _direct routing_.
You're probably raising up your stark fist right now, but fear not; there is a solution. I suspect the reason for this behavior is that inheritance would never work the way you are using it without API versioning. Web API would just barf and say the routes are ambiguous. To overcome the built in behaviors, you need to replace the IDirectRouteProvider with an implementation that will considered inherited attributes. I'll attached the world's simplest demo and it worked for me. Give it a go for yourself and let me know how it works out.
Did this solution work for you? Just want to make sure your issue was resolved before closing. Thanks.
Sorry, this didn't, tomorrow I will upload an example of what exactly I
need to do.
On Mon, Jun 19, 2017 at 9:44 PM Chris Martinez notifications@github.com
wrote:
Did this solution work for you? Just want to make sure your issue was
resolved before closing. Thanks.—
You are receiving this because you authored the thread.
Reply to this email directly, view it on GitHub
https://github.com/Microsoft/aspnet-api-versioning/issues/142#issuecomment-309620580,
or mute the thread
https://github.com/notifications/unsubscribe-auth/AIGiBy-c-NkBcTJgcRVO13Mcz1Bq3RhHks5sFyP9gaJpZM4N5FPi
.
This is the example app:
Call example to /api/v1/values

Call example to /api/v1/values/2

Call example to /api/v2/values

Call example to /api/v2/values/2

I looked your example, but the approach you are trying to use will never work. You can inherit an implementation, but you cannot inherit the API version mapping. Doing so would result in ambiguous routes.
Using your method (if I understand what you are asking):
/api/v1/values/api/v1/values/1 /api/v1/values/api/v1/values/1/api/v2/values/api/v2/values/1 Using my method (as previously posted)
/api/v1/values/api/v1/values/1 /api/v2/values/api/v2/values/1 I think I understand what you're trying to solve, but admittedly I'm not a fan of using inheritance to solve this problem. Regardless, it's impossible to have more than one controller type and/or action that map to the same route and API version combination. That will result in runtime failures because they are ambiguous.
For clarity and maintenance, my suggestion would be to only use inheritance to share common functionality among all your controllers and leave the routing and API version mapping explicit per controller. If all your routes will be the same and only the implementation varies by API version, then you can use the extensions I demonstrated to realize that. Other than that, I'm not sure how you can achieve your goal.
Let me know if I've misunderstood something.
Just a few things:
Using my method:
V1.ValuesController
GET = /api/v1/values
GET = /api/v1/values/1
V2.ValuesController
GET = /api/v2/values /// since this actions is overiden
GET = /api/v2/values/1 /// since this action is inherited from V1.ValuesController but not overiden
The only way I cand reach this is overriden Get(int id) method on V2.ValuesController but jus invoking base.Get(id), something like this:
V1.ValuesController
namespace WebApplication1.Controllers.V1
{
[ApiVersion("1")]
[RoutePrefix("api/v{version:apiVersion}/values")]
public class ValuesController : ApiController
{
// GET api/values
[HttpGet]
[Route("")]
public virtual IEnumerable<string> Get() => new[] { "value1 v1", "value2 v1" };
// GET api/values/5
[HttpGet]
[Route("{id:int}")]
public virtual string Get(int id) => $"value{id} v1";
}
}
V2.ValuesController
namespace WebApplication1.Controllers.V2
{
[ApiVersion("2")]
[RoutePrefix("api/v{version:apiVersion}/values")]
public class ValuesController : V1.ValuesController
{
// GET api/values
[Route("")]
public override IEnumerable<string> Get() => new[] { "value1 v2", "value2 v2" };
[Route("{id:int}")]
public override string Get(int id) => base.Get(id);
}
}
Of course this dont look very useful, but in a real scenario the response will be a model that could be the same in each version..
Correct; that will work. If you use extended IDirectRouteProvider I provided, you can get away with only overriding the methods; you won't have to re-specify the [RoutePrefix] and [Route] attributes. Beyond that I think you may be up against how Web API works.
My suggestion would be to make a collaborator the thing that is reused rather than the controller. IMO, it will also make it easier to rationalize what a controller implements when someone looks at the source.
For example:
```c#
namespace V1
{
[ApiVersion( "1.0" )]
[RoutePrefix( "api/v{version:apiVersion}/values" )]
public class ValuesController : ApiController
{
readonly IRepository
public ValuesController( IRepository
[Route]
public async Task<IHttpActionResult> Get() => Ok( await repository.GetAll() );
[Route( "{id:int} " )]
public async Task<IHttpActionResult> Get( int id ) => Ok( await repository.GetSingle( id ) );
}
}
namespace V2
{
[ApiVersion( "2.0" )]
[RoutePrefix( "api/v{version:apiVersion}/values" )]
public class ValuesController : ApiController
{
readonly IRepository
public ValuesController( IRepository
[Route]
public async Task<IHttpActionResult> Get() => Ok( await repository.GetAll() );
[Route( "{id:int} " )]
public async Task<IHttpActionResult> Get( int id ) => Ok( await repository.GetSingle( id ) );
}
}
```
Your repository and/or model might change between API versions or you might have multiple repositories.
Naturally, this is just one more variation of numerous possibilities. There are several solutions here that will work, but I'm not sure any of one of them individually gets you _everything_ you want. I think you're reaching the edge of what's possible without changing a significant amount of Web API plumbing.
Ok, thanks!
The problem will go away if you do something like this i guess. @jcmordan
[RoutePrefix( "api/v{version:apiVersion}/[controller]/[action]" )]
Thanks for the suggestion @irowbin , but unless I'm wrong the support for the [controller] and [action] replacement tokens were added in ASP.NET Core. This context here is for Web API. Your suggestion _should_ work in Core.
It seems like you have what you need @jcmordan . If you continue to run into any issues. Feel free to reopen this issue. Thanks.
This is very interesting topic! I am looking for a solution how that gives me a possibility to override just some endpoints in the version 2. I have tried to modify you solution, but it the routing of not overided endpoints fails with this error. I don't really want to do an override for each endpoint in version 2, if I need to change the functionality of just one endpoint.
The way how I am looking to API versions is, when I am creating new endpoints (extending the functionality) it is part of version 1 as it does not influence existing applications. Each new version is for overriding the existing endpoints and giving them new/different functionality.
No route providing a controller name with API version '2' was found to match request URI 'http://localhost:21956/api/v2/values/4'.
I am also attaching adjusted version of my desired setup
InheritanceDemo.zip
And desired setup, so not everyone needs to download the solution
```c#
namespace Demo.Controllers.V1
{
[ApiVersion( "1.0" )]
[RoutePrefix( "api/v{version:apiVersion}/values" )]
public class ValuesController : ApiController
{
[Route]
[MapToApiVersion( "1.0" )]
public virtual IHttpActionResult Get( ) => Ok( new[] { "value.1.v1", "value.2.v1", "value.3.v1" } );
[Route( "{id:int}" )]
public virtual IHttpActionResult Get( int id ) => Ok( $"value.{id}.v1" );
}
}
```c#
namespace Demo.Controllers.V2
{
[ApiVersion( "2.0" )]
public class ValuesController : V1.ValuesController
{
[MapToApiVersion( "2.0" )]
[Route]
public override IHttpActionResult Get( ) => Ok( new[] { "value.4.v2", "value.5.v2", "value.6.v2" } );
//this should be the same as the version 1, so I don't need to do an override
//and I don't really do an override of each action if there was like a hundred actions and I would need to override just 1
//public override IHttpActionResult Get( int id )
}
}