Giter Site home page Giter Site logo

Comments (17)

commonsensesoftware avatar commonsensesoftware commented on May 22, 2024 1

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.

commonsensesoftware avatar commonsensesoftware commented on May 22, 2024

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.

commonsensesoftware avatar commonsensesoftware commented on May 22, 2024

FYI, the sample has been updated.

from aspnet-api-versioning.

koo9 avatar koo9 commented on May 22, 2024

Awesome, thx Chris. I try to make this namespace versioning to work with attribute routing.

from aspnet-api-versioning.

koo9 avatar koo9 commented on May 22, 2024

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.

koo9 avatar koo9 commented on May 22, 2024

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.

commonsensesoftware avatar commonsensesoftware commented on May 22, 2024

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.

commonsensesoftware avatar commonsensesoftware commented on May 22, 2024

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.

koo9 avatar koo9 commented on May 22, 2024

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.

koo9 avatar koo9 commented on May 22, 2024

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.

commonsensesoftware avatar commonsensesoftware commented on May 22, 2024

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.

koo9 avatar koo9 commented on May 22, 2024

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.

commonsensesoftware avatar commonsensesoftware commented on May 22, 2024

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.

koo9 avatar koo9 commented on May 22, 2024

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.

koo9 avatar koo9 commented on May 22, 2024

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.

commonsensesoftware avatar commonsensesoftware commented on May 22, 2024

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.

koo9 avatar koo9 commented on May 22, 2024

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)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo 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.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.