I have integrated Swagger to my versioned API successfully and have been playing with creating different versions of controllers. I wanted to mark a particular controller as ApiVersionNeutral, which works fine from my test client, but the controller then disappears from Swagger - understandable, as I can request the version in Swagger and so it makes sense that something without a version doesn't appear.
So, my question is - is there any way to make neutral controllers appear regardless of what version has been requested through Swagger?
Never mind, I think I have worked this out - I need to amend the way my RoutePrefix works, as I currently have the same prefix across different controllers which is a mistake. I will close this issue as I believe this to be user error - sorry!
You didn't specify if this was ASP.NET Web API or Core. I did a sanity check and it appears that the Web API version does have a bug that doesn't honor API version neutrality.
One thing that can be confusing about API versioning is that once you enable it, everything has an API version. There is really no such thing as _no API version_. Despite my effort to use a clear name, I failed. _API version-neutral_ does not mean _unversioned_. Instead, it means _every version_ (e.g. neutral or _I don't care which version_). This should mean that a version-neutral API should appear for every known version.
I was just about to re-open, as what I thought I had done wrong hasn't in fact fixed the issue!
To clarify, I'm using ASP.NET Web API (not Core).
To clarify what I'm trying to achieve:
I have a controller called AuditController, which now has a V2 method.
There is another controller called EntityTypesController. Nothing in this has changed with the new version, so I can just append ApiVersion("2.0") and this works fine.
I then considered that, rather than having to append another ApiVersion attribute every time a new version of some other controller is released, if nothing has changed in EntityTypesController then I could simply use [ApiVersionNeutral] - and keep using this until the point at which I need to version the EntityTypesController.
It works fine when I tested this from my test client consuming the API, but I couldn't then see the controller in the Swagger documents - which may be the bug you talk about above.
Hope that adds some clarification!
Just as a further explanation - I'm trying to avoid my clients needing to pass different versions depending on what service call they are making. This is a gateway api, and I essentially want the client to be able to just specify what "version of the API" they are using once, for every request.
In other words, if a client says they are on version 2 and wants audits, they'll get the V2 audit object. But if they then request EntityTypes, event though they will still pass api-version 2 in the request, they will just get the "VersionNeutral" entity types controller - until such point that I have to amend the EntityTypesController, in which case I will remove the ApiVersionNeutral attribute and replace it with specific ApiVersion attributes instead.
I hope I'm making sense(!)
For documentation, it's definitely an issue. It was a simple oversight on my part. I'm in the process of working on a fix.
In terms of your approach to address your versioning scenarios, this is a plausible solution. Kind in mind, however, that by going version-neutral the controller will accept any version, even ones that that you don't necessarily want to support. This might be fine now, but it could come back to haunt you. I had that happen with one team. They went from being version-neutral to versioned, which one would think should be ok if the client is passing a version. In that case, the clients were, in fact, passing an API version, but it wasn't a supported API version. It was some arbitrary API version that was being accepted and ultimately broke once a specific API version was required. Be careful with being version-neutral if you think you might ever become versioned - there be dragons.
There are some other alternatives to achieving your goal:
The IApiVersionSelector was specifically meant to help address the scenario you are describing. The single method SelectVersion accepts the current HttpRequestMessage and an aggregation of all discovered API versions (so you don't have to do the hard work). You can then choose the best API version given the API version model or some other information from the current request. In your case, it would be the request itself for one specific route. The IApiVersionSelector.SelectVersion method is only called when no API version is specified and implicitly API versioning is allowed.
This approach can be taken to the extreme, which would be to create a version mapping between clients and implemented API versions. Once a client calls for the first time, they are bound to the API version they matched to. From that point forward, they will always get that version. You'd have to write some bits to store this mapping and you'd want to pre-load it some where in the pipeline, but it is possible. If you have external storage holding this mapping, you'll definitely want to do it outside the IApiVersionSelector because it's design is [intentionally] not asynchronous.
If this is something you are interested in pursuing, you can find more information in the API Version Selector topic. Their implementations are pretty simple. Feel free to checkout the source for how to implement your own.
I hope that helps.
Wow that is super helpful, thank you - I will look further into the IApiVersionSelector as you suggest. This is a fairly new api and not yet fully out in the wild, so it will be good to get this right up front.
Thanks again!
Hi guys,
Can I find any example on how to expose an ApiVersionNeutral Controller in Swagger?
Sorry guys if this is not the proper place to ask.
Thank you
An API _version-neutral_ controller will appear in every Swagger document (which is per API version). I'm not sure if there is an example for vanilla MVC, but there is an example for OData. You can see the Web API example here, but there's a similar equivalent for Core too.
There is a curious case of all your controllers being version-neutral (which sounds like a really, really bad idea). In this case, you should only have a single API version, which would be defined by ApiVersioningOptions.DefaultApiVersion. All of the controllers would be in this version.
If this is somehow not the case for you, but you have repro you can share (best) or at least your setup, then some better guidance can be provided.
An API _version-neutral_ controller will appear in every Swagger document (which is per API version). I'm not sure if there is an example for vanilla MVC, but there is an example for OData. You can see the Web API example here, but there's a similar equivalent for Core too.
There is a curious case of all your controllers being version-neutral (which sounds like a really, really bad idea). In this case, you should only have a single API version, which would be defined by
ApiVersioningOptions.DefaultApiVersion. All of the controllers would be in this version.If this is somehow not the case for you, but you have repro you can share (best) or at least your setup, then some better guidance can be provided.
Hi Chris,
Thank you so much for your fast reply.
For some reason the controller does not appear in swagger.
My setup is the following https://codeshare.io/5DMNOr .
The goal is to have the version 1 and 2, and have some methods that does not depend on the version.
I am using the following attributes in my controllers:
[ApiVersion("1.0")]
[ApiVersion("2.0")]
[RoutePrefix("api{version:apiVersion}/{controller}")]
[ApiVersionNeutral]
[RoutePrefix("api/{controller}")]
Thanks for the help!
Does accessing your API even work? I would not expect it to with this configuration. A controller cannot be simultaneously versioned and version-neutral. This would be ambiguous and should result in a 500.
You _should_ now be able to version down to the action level. This would not be ambiguous because the action level versioning is explicit and trumps whatever is defined at the controller level. The most obvious case for this would be DELETE, which is unlikely to ever change across API versions.
I would start with those modifications and I'd also verify you can even call the API (via Postman or something), if you haven't already. If things still aren't working, then it's possible there is an issue with this particular setup that hasn't been caught before - the test matrix of combinations is quite large. Versioning, and hence API exploration, down to the action level is _supposed_ be supported, even in Web API.
Let me know.
not
Sorry I didn't explain myself right. I wanted to say that I use those attributes but in different controllers.
I have the following folders:
Controllers - Here I use [ApiVersionNeutral] for each controller
V1/Controllers - Here I use [ApiVersion("1.0")] for each controller
V2/Controllers - Here I use [ApiVersion("2.0")] for each controller
Detected the problem now, I had a typo in the controller suffix name (blindly I didn't detect it) and that's why it was not appearing in swagger, it had nothing to do with ApiVersionNeutral.
Sorry for the inconvenient.
One more question, is it possible to place the controller swagger doc in some separated section instead of appearing in every version?
Thank you.
Ah … awesome. Glad you got it working.
By _separate section_, do you mean place all version-neutral APIs in a single document? If yes, that is technically possible, but not supported out-of-the-box. You'd have to extend the VersionedApiExplorer and recollate the results before the Swagger generator uses the discovered action descriptors or provide a Swagger generator extension/customization that recollates the results from the IApiExplorer before generating documents. In Web API and Swashbuckle, you might be able to achieve this via the MultipleApiVersions callback.
Remember that a version-neutral API accepts any and all API versions, including none at all. Since the Swagger documents are generated according to API version, there isn't a _nice_ way to have these tallied together. Furthermore, Swagger/OpenAPI documents generated this way are meant to describe the API surface area by API version. A consumer is looking at all the APIs in a particular version. A version-neutral API _should_, therefore, appear in every document because it applies to every API version.
While it should be possible to bucketize things the way you want, do keep in mind that a client will have to stitch the set of versioned and version-neutral APIs together. The default behavior collates all applicable APIs together per API version. Also keep in mind that a version-neutral API is orthogonal to a client - e.g. it's more of an implementation detail. Unless you allow the client to not specify an API version (which you _should_ never allow), they don't know that behind the scenes their request is going to the same implementation; regardless of the API version they specify. Finally, when it comes to a Swagger/OpenAPI document, a version-neutral API still requires an API version by default. By putting things into their own bucket, you are inviting clients to specify bogus API versions that would likely otherwise be avoided. This _could_ lead to problems down the road. Keep in mind that when a client sees a version-neutral API in the Swagger document, they don't know that it's version-neutral; they just know it appears in every document and API version.
I hope that helps.
Ah … awesome. Glad you got it working.
By _separate section_, do you mean place all version-neutral APIs in a single document? If yes, that is technically possible, but not supported out-of-the-box. You'd have to extend the VersionedApiExplorer and recollate the results before the Swagger generator uses the discovered action descriptors or provide a Swagger generator extension/customization that recollates the results from the IApiExplorer before generating documents. In Web API and Swashbuckle, you might be able to achieve this via the
MultipleApiVersionscallback.Remember that a version-neutral API accepts any and all API versions, including none at all. Since the Swagger documents are generated according to API version, there isn't a _nice_ way to have these tallied together. Furthermore, Swagger/OpenAPI documents generated this way are meant to describe the API surface area by API version. A consumer is looking at all the APIs in a particular version. A version-neutral API _should_, therefore, appear in every document because it applies to every API version.
While it should be possible to bucketize things the way you want, do keep in mind that a client will have to stitch the set of versioned and version-neutral APIs together. The default behavior collates all applicable APIs together per API version. Also keep in mind that a version-neutral API is orthogonal to a client - e.g. it's more of an implementation detail. Unless you allow the client to not specify an API version (which you _should_ never allow), they don't know that behind the scenes their request is going to the same implementation; regardless of the API version they specify. Finally, when it comes to a Swagger/OpenAPI document, a version-neutral API still requires an API version by default. By putting things into their own bucket, you are inviting clients to specify bogus API versions that would likely otherwise be avoided. This _could_ lead to problems down the road. Keep in mind that when a client sees a version-neutral API in the Swagger document, they don't know that it's version-neutral; they just know it appears in every document and API version.
I hope that helps.
Thank you so much for your time, very helpful explanation.
In this case I do allow to not specify version since I am using ApiVersionNeutral for more generic endpoints like HealthCheck, not sure if it is worth to implement those changes though.