aspnet-api-versioning: WebAPI Versioning with Swagger Error (ApiVersion unspecified)
VERSION:
Swashbuckle: 5.6.0 AspNet.WebApi.Versioning 2.2.0
STEPS TO REPRODUCE:
Consume any endpoint (postman or swagger)
EXPECTED RESULT:
Hits the actual endpoint
ACTUAL RESULT:
Receive the error: ApiVersionUnspecified
ADDITIONAL DETAILS
Using Owin with WebAPI. Owin’s Setup is blank. Global.asax.cs:
protected void Application_Start(object sender, EventArgs e) { GlobalConfiguration.Configure((GlobalConfiguration.Configuration.DependencyResolver.GetService(typeof(WebApiConfig)) as WebApiConfig).Register); }
WebApiConfig.cs:
public void Register(HttpConfiguration config) { var constraintResolver = new DefaultInlineConstraintResolver { ConstraintMap = { [“apiVersion”] = typeof(ApiVersionRouteConstraint) } }; config.MapHttpAttributeRoutes(constraintResolver); config.AddApiVersioning(o => o.ReportApiVersions = true); this.restApiConfig.Register(config); }
Controller route formatting:
[ApiVersion(“3.0”)] [RoutePrefix(“v3”)] public class SomeController {
[Route(“/someRoute”)] public HttpResponseMessage GetStuff(){ }
We have attempted to use the [RoutePrefix(v{version : apiVersion)] attribute, which works with actually making the endpoints function under versioning, but forces Swagger to include a version
parameter, which we do not want. We want our paths to be “/v2/controller/action” or “/v3/controller/action”.
Happy to use the [RoutePrefix(v{version : apiVersion)]
method, as long as we can display the paths properly in Swagger without having to input a version
parameter. Otherwise, if we could use the [RoutePrefix("v3")]
method without getting ApiVersionUnspecified
errors, that would be even better.
Is there a way to use our preferred [RoutePrefix("v3")]
method?
About this issue
- Original URL
- State: closed
- Created 6 years ago
- Comments: 19
API versioning does not know about nor parse magic strings. It uses a route constraint so that you can define where and how the value appears in your URLs. In a similar way, the prefix
v
is not actually part of an API version. In order for the infrastructure to pick up and validate the API version when versioning by URL segment, you must use the route constraint; otherwise, API versioning can’t extract the value from the request.From your description, the issue is really with Swagger/Swashbuckle. The API Explorer provided by API versioning will generate the appropriate parameter and default value for the API version route parameter. However, if that’s now how you want to see that in Swagger, then you’ll need some customization to transform the operation which Swashbuckle is generating the document. Any time that Swashbuckle passes you an ApiDescription, this will actually be an instance of the super type VersionedApiDescription. Casting to VersionedApiDescription will give you access to the corresponding ApiVersion as well as the RelativePath (ex:
v{version}/someRoute
). Some minor string manipulation will allow you to update the RelativePath with the corresponding API version value populated. You can format the API version yourself or rely on the supported, built-in formatting capabilities. You’ll also want to remove the parameter definition for the API version.Another possibility is subclass the VersionedApiExplorer, override the ApiDescriptions, and perform the same transformation. You could also use the Decorator pattern to create an IApiExplorer implementation that wraps over VersionedApiExplorer and performs the transformation. There isn’t anything special about wiring up an API Explorer. The extension methods are just shortcuts. You custom implementation might look like:
Yet one more way to solve the problem is to disable the input for the API version parameter in the Swagger in the UI. I don’t believe Swashbuckle has the capability to mark a parameter as read-only, but this should be technically feasible. If the value is read-only, I’m not sure that it’s worth the effort to customize other pieces because the constructed URL will be correct. I suppose it’s possible to make the input for the parameter hidden as well.
As you can see, there are a few ways to achieve your desire result. Choose whichever works best for you.
I hope that helps.
FYI, there is a new version of the API Explorer package available. I’ve added an option called
SubstituteApiVersionInUrl
, which enables the behavior you want without further modification.The corresponding wiki topic and examples now reflect this too.
Adding a
new UrlSegmentApiVersionReader()
provides Swagger with the necessary method for getting the version.This works as expected…
Do you have controllers coming from an external source (e.g. package or something)? Those should not be duplicated.
Aside from the nuclear option like debugging with Reflector, you can try whipping together and replacing the DefaultDirectRouteProvider.
Then you can register it like:
This won’t solve the problem, but it will allow you to debug and trace all the routes being generated from attributes (formally known as direct routes). That should help identify the offending controller.