Comments (17)
Great. To version by namespace and use attribute routing in Web API, you just need to register the route constraint in the configuration.
var constraintResolver = new DefaultInlineConstraintResolver()
{
ConstraintMap =
{
["apiVersion"] = typeof( ApiVersionRouteConstraint )
}
};
configuration.MapHttpAttributeRoutes( constraintResolver );
After that, things will be pretty standard in your controllers. It should look something like:
namespace Services.V1.Controllers
{
[ApiVersion( "1.0" ), Route( "api/v{version:apiVersion}/my" )]
public class MyController : ApiController { /* omitted */ }
}
namespace Services.V2.Controllers
{
[ApiVersion( "2.0" ), Route( "api/v{version:apiVersion}/my" )]
public class MyController : ApiController { /* omitted */ }
}
namespace Services.V3.Controllers
{
[ApiVersion( "3.0" ), Route( "api/v{version:apiVersion}/my" )]
public class MyController : ApiController { /* omitted */ }
}
from aspnet-api-versioning.
This is actually the correct behavior. I just realized that the comments in the code are not correct. Despite the route configuration indicating that the accountId route parameter is optional, it isn't. You may have noticed that each version of the service has only a single GET action:
public IHttpActionResult Get( string accountId ) => Ok( /* omitted */ );
This action will match GET /v1/agreements/42
, but not GET /v1/agreements
. In order for the accountId route parameter to be optional, it needs to be declared as string accountId = null
.
I will update the sample so that the comments and route map match the expected behavior. Thanks for reporting this. I hope that helps.
from aspnet-api-versioning.
FYI, the sample has been updated.
from aspnet-api-versioning.
Awesome, thx Chris. I try to make this namespace versioning to work with attribute routing.
from aspnet-api-versioning.
hi Chris,
I try to make it work with Swagger.
with the constraint resolver, it might work, but soon as the Route( "api/v{version:apiVersion}/my" ) on, the controller with the same name in different namespace wont' show up. but if I do this Route( "api/v1/my" ), it will work. now it might have something to do with the resolver that does not make the two controllers unique for the ApiExplorer?
adding the routeprefix Route( "api/v{version:apiVersion}/my" ), the version become a mandatory input parameter for all method. that's odd.
also even with the Route( "api/v1/my" ) config to make both controller show up on swagger, when invoking the api through the swagger ui will got an error saying
{
"Message": "No HTTP resource was found that matches the request URI 'http://localhost/link/v1/insurance/groups/1/certificates/2'.",
"MessageDetail": "No route providing a controller name was found to match request URI 'http://localhost/link/v1/insurance/groups/1/certificates/2'."
}
from aspnet-api-versioning.
try to extend the ApiVersionControllerSelector. turns out everything else is internal. I guess there little use of making virtual methods in these selectors
from aspnet-api-versioning.
I've provided some pretty detailed responses to supporting API documentation in #46 and #54. Succinctly, I will say that there is no out-of-the-box support for API documentation - yet. It's something that I've thought about and there are APIs to query the models, but that has not formalized into any included functionality. I will open up some issues to track these requests.
If the existing comments for issues #46 and #54 don't get you want you need, I'm happy to answer more questions. It may be somewhat involved on your side, but it's definitely possible to get all of the API version information for each controller without subclassing anything.
from aspnet-api-versioning.
The ApiVersionControllerSelector does have some virtual methods, but as you mention, they aren't terribly useful. This is largely the same surface area as the original DefaultControllerSelector.cs implementation. I'm not opposed to opening things up, but - honestly - it's a deep, dark path that you don't really want to go down. Controller and action selection is at the heart of what the Web API library for API versioning provides.
If you can articulate what you'd like to get out of subclassing the ApiVersionControllerSelector, I can probably explain how to achieve your goals without using inheritance.
from aspnet-api-versioning.
to fix the issue on hand, I need to override the SelectController method in ApiVersionControllerSelector class. since Route( "api/v{version:apiVersion}/my" ) does not work for the same controller name in two different version folder. I need to modify
EnsureRequestHasValidApiVersion( request );
to get the version info from the request url (path not the querystring) if all the existing logic failed.
then override the GetControllerName method to add
var attributedRoutesData = request.GetRouteData().GetSubRoutes(); var subRouteData = attributedRoutesData.FirstOrDefault(); if (subRouteData != null) { var actions = (HttpActionDescriptor[]) subRouteData.Route.DataTokens["actions"]; var controllerName = actions?[0].ControllerDescriptor.ControllerName; return controllerName; }
if all logic fail to get the correct controller name. I haven't really look into the implementation detail yet. so i am not sure if these are the right place for modification but so far it's working.
from aspnet-api-versioning.
with all that, it works perfectly with swashbuckle. i even customize the swagger UI for version selection but I don't want to maintain the customized code base of your lib here. just want to add couple of classes to inherit from the existing class to get what I want.
from aspnet-api-versioning.
I've added #59 and #60 to track these issues and enable others, like yourself, to post feedback on.
I'm not sure I completely follow about the same route for two different controllers in different namespaces. If your controller type names are the same, then that will make things easier for documentation. The design of Web API doesn't support the notion of multiple controller types (e.g. descriptors) for a given controller name. I was able to make this work, however, by creating a custom HttpControllerDescriptorGroup. This is a custom HttpControllerDescriptor that aggregates all of the descriptors together. This is not something easily disseminated or inferred from the implementation, but it's the only thing I could come up without breaking the existing Web API interfaces.
This means you can flatten and traverse the descriptors like so:
var controllerMapping = configuration.Services.GetHttpControllerSelector().GetControllerMapping();
var controllerDescriptorGroup = controllerMapping["My"];
var controllerDescriptors = controllerDescriptorGroup as IEnumerable<HttpControllerDescriptor> ??
new []{ controllerDescriptorGroup };
foreach ( var controllerDescriptor in controllerDescriptors )
{
// TODO: enumerate each implementation of "My" service
}
This is just the tip of the iceberg. Issues #46 and #54 go into much more detail about the approaches and how to get at the API version models for controllers and their actions. Using the ApiVersionControllerSelector should not be necessary at all for documentation purposes.
from aspnet-api-versioning.
it' s not the same route for the two different controllers in different namespace. it's different route marked by routeprefix "v1/my" and "v2/my". notice it's not using the apiVersion route constraint as you suggested in the routeprefix because when using that, the two controllers wont' show up in swagger and somehow the version become a mandatory input parameter in all methods within the controller.
from aspnet-api-versioning.
Yes ... the apiVersion route constraint will be mandatory. To resolve the route, you can use the approach above. You can aggregate the ApiVersionModel for a given logical service by name. Using the aggregated models, you can fill-in the route template parameter.
Expanding from above:
var serviceVersionModel = controllerDescriptors.Select( cd => cd.GetApiVersionModel() ).Aggregate();
var routeValues = new RouteValueDictionary() { /* you other route template parameter values */ };
foreach ( var apiVersion in serviceVersionModel.ImplementedApiVersions )
{
routeValues["version"] = apiVersion.ToString();
IHttpRoute route = ResolveRoute( configuration, routeValues );
// TODO: route paired with descriptor and API version
}
You can try to match routes to controllers based off of the route prefix, but that will be magic string matching. You're better off using the UrlHelper or other components to provide the API version for the route you want to resolve as a route template value. Just a suggestion.
from aspnet-api-versioning.
so after all I need to modify your code base instead of writing couple of classes. do you think open up the internal classes an option?
from aspnet-api-versioning.
I think the version route constraint is a bit intrusive. don't you think? it added overhead for the caller. instead just putting the version info in the routeprefix as is e.g. RoutePrefix["v1/blah/"], it's just a matter of getting the version info from the routeprefix. because the routeprefix is kind of mandate the url to have the version info there. so the caller will follow the convention and state their intend in the url.
from aspnet-api-versioning.
Using a route constraint removes the magic string interpretation in URL. The infrastructure cannot determine where in the URL the version segment is. Consider the difference between v1/blah
and api/v1/blah
. The location of the version segment is not guaranteed. The API versioning support never assumes or tries to infer the API version from the URL using Regex or some other string matching.
In terms of filling in the route template, it's no different than filling in the controller or id parameters. You can choose to fill in the parameters from well-known data or you can discover it dynamically from the meta-model.
[ApiVersion( "2.0" )]
[RoutePrefix( "api/v{version:apiVersion}/blah" )]
public class BlahController : ApiController
{
[Route( Name = "Blah-V2" )]
public IHttpActionResult Get() => Ok();
[Route( "{id:int}", Name = "BlahById-V2" )]
public IHttpActionResult Get( int id ) => Ok();
}
Figure 1: Basic controller with versioned URL segment
To generate a URL, you would just need:
UrlHelper url = null; // get or create UrlHelper
var link = url.Link( "BlahById-V2", new { version = "1", id = 42 } );
Figure 2: Build a URL
Admittedly, I'm not a fan of API versioning by URL path segment. URLs should be stable over time IMHO. Documentation and client usage is easier by query string or even header because the URL and route never changes. Since a header or query parameter are not part of the URL path, they also don't identify a resource by definition.
One alternative that might make things easier for you using this approach would be to use convention-based routing instead of attribute routing. I really like attribute routing, but convention-based routing would probably make the documentation process much easier since there would be only a single route with a single name.
from aspnet-api-versioning.
I see your point but things always change as everything else in this universe :)
I try attribute routing and made it work nicely but the requirement is to keep the same controller name between versions. hence i found this nice lib you made. excellent job btw .
Sent from my Samsung device
-------- Original message --------
From: Chris Martinez [email protected]
Date: 11-11-2016 6:06 PM (GMT-07:00)
To: Microsoft/aspnet-api-versioning [email protected]
Cc: Kevin [email protected], State change [email protected]
Subject: Re: [Microsoft/aspnet-api-versioning] error with bynamespacewebapi sample (#58)
Using a route constraint removes the magic string interpretation in URL. The infrastructure cannot determine where in the URL the version segment is. Consider the difference between v1/blah and api/v1/blah. The location of the version segment is not guaranteed. The API versioning support never assumes or tries to infer the API version from the URL using Regex or some other string matching.
In terms of filling in the route template, it's no different than filling in the controller or id parameters. You can choose to fill in the parameters from well-known data or you can discover it dynamically from the meta-model.
[ApiVersion( "2.0" )]
[RoutePrefix( "api/v{version:apiVersion}/blah" )]
public class BlahController : ApiController
{
[Route( Name = "Blah-V2" )]
public IHttpActionResult Get() => Ok();
[Route( "{id:int}", Name = "BlahById-V2" )]
public IHttpActionResult Get( int id ) => Ok();
}
Figure 1: Basic controller with versioned URL segment
To generate a URL, you would just need:
UrlHelper url = null; // get or create UrlHelper
var link = url.Link( "BlahById-V2", new { version = "1", id = 42 } );
Figure 2: Build a URL
Admittedly, I'm not a fan of API versioning by URL path segment. URLs should be stable over time IMHO. Documentation and client usage is easier by query string or even header because the URL and route never changes. Since a header or query parameter are not part of the URL path, they also don't identify a resource by definition.
One alternative that might make things easier for you using this approach would be to use convention-based routing instead of attribute routing. I really like attribute routing, but convention-based routing would probably make the documentation process much easier since there would be only a single route with a single name.
You are receiving this because you modified the open/close state.
Reply to this email directly, view it on GitHubhttps://github.com//issues/58#issuecomment-260090343, or mute the threadhttps://github.com/notifications/unsubscribe-auth/AICCEkqU_6_YRtirtPcXRyBu6etkwE2vks5q9REVgaJpZM4KvUBI.
from aspnet-api-versioning.
Related Issues (20)
- OpenAPI not working with version interleaving within single controller HOT 3
- ActionDescriptor no longer returns an ApiVersionModel property HOT 2
- ApiVersioningOptions UseApiBehavior removal HOT 3
- ASP.NET Web API versioning Migrate from QueryStringApiVersionReader to UrlSegmentApiVersionReader HOT 2
- only the 5.1.0 version of Microsoft.AspNetCore.Mvc.Versioning is deprecated HOT 3
- Problem with describing reponse codes in minimal api HOT 3
- Cannot run APIs with different controller names with same ControllerName attribute after migration HOT 6
- Different options in `ApiVersioningOptions.cs` between .NET Framework and .NET Core packages HOT 2
- WithOpenApi() ignore Api versioning readers HOT 4
- .net 8 support HOT 7
- Breaking changes when migrating to OData8 + new versioning HOT 10
- odata/$metadata returns 404 when all controllers are decorated with ApiVersionNeutralAttribute HOT 3
- AddVersionedApiExplorer not working in Asp.Versioning HOT 5
- VersionedApiDescriptionProvider does not set the correct SunsetPolicy to ApiDescription instances HOT 1
- Using ApiExplorerSettingsAttribute together with ApiVersionAttribute produces unexpected number of ApiVersionDescriptions HOT 5
- Asp.Net Core WebApi - AWS ECS Cluster Authentication failure HOT 2
- [Versioned Clients][API Notifications] Fails to read new versions when available HOT 2
- Swashbuckle documentation inconsistent with examples HOT 2
- AssumeDefaultVersionWhenUnspecified does not work correctly if ApiVersionNeutral is used in the controller HOT 2
- swagger.json file not found after update HOT 9
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from aspnet-api-versioning.