-
-
Notifications
You must be signed in to change notification settings - Fork 1.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add ApiGroupNames setting, remove ApiVersionProcessor and improve api versioning #1701
Conversation
@commonsensesoftware now one of the problems i have is that even if i set |
Ah … this is because the IApiVersionReader supports composition (e.g. combining methods together). The query string method is encouraged as the default behavior, but the URL segment method is so common it's also enabled by default because it doesn't have any visible side effects to the query string method. Unfortunately, the inverse is not true. What you're observing is the api-version parameter reported for the query string. To limit things to only the URL segment method, change the configuration as: AddApiVersioning(options =>
{
options.ApiVersionReader = new UrlSegmentApiVersionReader();
}) |
@commonsensesoftware thanks for the info. PR looks fine now... |
@RSuter I can't find the |
It's in the See sample project: |
@OculiViridi Make sure you are adding MvcCore directly before calling If you need the full MVC package, then: |
Ref: #1355 |
Hi All, Per #2325 I'm still having issues with 2.2 and API versioning. When trying to use I was hoping migration to NSwag would be easy... |
You shouldn't have to use In attempting to stitch the two issues together, it would appear that your group names are incongruent. That would explain why things do not match up. Swagger generators and API Versioning do not know about each other. API Versioning collates and groups actions according to their API versions. Swagger generators simply use the grouped actions defined by the API Explorer - however that magically happened. I see that your document has the Name and Version of For more information, review API Version Formatting. I hope that helps. |
@commonsensesoftware Thanks for the reply. I read the docs as linked. Yes, in my eagerness to migrate to NSwag, I'd not correctly decoded, the other implementation. with services.AddVersionedApiExplorer(options =>
{
options.GroupNameFormat = "VVV";
options.SubstituteApiVersionInUrl = true;
});
...
services.AddApiVersioning(options =>
{
options.ReportApiVersions = true;
options.AssumeDefaultVersionWhenUnspecified = true;
options.DefaultApiVersion = new ApiVersion(1, 0);
}); Swashbuckles: services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new Info { Title = "My Title API", Version = "v1" });
}); translates to: services.AddOpenApiDocument(document =>
{
document.DocumentName = "v1";
document.Version = "v1";
document.Title = "My Title API";
document.ApiGroupNames = new[] {"1"};
}); In the hopes that this help other migrators. As a though... it might be a good idea to retrieve the var explorer = services.BuildServiceProvider().GetRequiredService<IApiDescriptionGroupCollectionProvider>(); I'd figure that a useful model would be to loop though each group identified in the explore and add a However it seems the explorer at the point provides an empty collection, and figure this is because the startup has not proceeded far enough in the build process as yet to have the API Version information generated from the MVC code...where the Swashbuckle implementation would otherwise do this in the |
@acds, the IApiVersionDescriptionProvider service defined by API Versioning will give you a simpler and fully aggregated view of the information you're looking for. The Swashbuckle setup used to be similar to the same setup you are describing here using I don't know enough about NSwag to tell you if this same concept is supported for configuring I hope that's useful. |
Is there any updates on iterating through the IApiVersionDescriptionProvider? I've been looking for a more elegant solution, but this seems to be the only way still?
|
@ThomasVague Are you looking for something beyond |
Well, not really. This works just fine. I was just curios to see if there had been any changes since the post was made. I was just browsing different setup methods, and I came over this example for Swashbuckle, and started wondering if NSwag had something similar. However, I am curios if there's any way to group documents even further. Let's say I want to group my controllers / namespaces into multiple documents for each version, so one document for version 1 with a subset of controllers, and another document for version 1 with another subset of controllers, and so on, for each existing version. Thanks in advance :) |
I can't speak to the speak hooks for NSwag, but it should certainly possible to hook up the API Versioning metadata in a similar way as Swashbuckle. There are no special handling or conditions for specific OpenAPI generators, including Swashbuckle. If there are some community examples of NSwag with API Versioning, I'm willing to link to them in the documentation. Unfortunately, you can't really group further; at least, not without some serious work. The issue isn't the code, NSwag, or API Versioning. The OpenAPI (formerly Swagger) UI only affords for a single level of grouping. Numerous changes would have to be made in order to make it work - likely more than you're willing to do. Another alternative is that you can combine the components together as a single string. For example:
That's probably not what you're hoping for, but that will work with minimal effort. Yet one more option is to create your own UI and then you can group however you like. |
That's actually exactly what I'm looking for. Only a single level, but grouped in versions and namespaces. I've been looking around, but haven't found any examples to do grouping in that manner. Currently my code looks like this : services.AddVersionedApiExplorer(o =>
{
o.SubstituteApiVersionInUrl = true;
o.ApiVersionParameterSource = new UrlSegmentApiVersionReader();
o.GroupNameFormat = "'v'VVV";
});
var versionDescriptionProvider = services.BuildServiceProvider().GetService<IApiVersionDescriptionProvider>();
foreach (var apiVersionDescription in versionDescriptionProvider?.ApiVersionDescriptions)
{
services.AddOpenApiDocument((document, serviceProvider) =>
{
document.DocumentName = "v" + apiVersionDescription.ApiVersion.MajorVersion;
document.ApiGroupNames = new string[] { "v" + apiVersionDescription.ApiVersion.MajorVersion };
document.Version = apiVersionDescription.ApiVersion.MajorVersion + "." + apiVersionDescription.ApiVersion.MinorVersion;
document.Description = settings.Description;
if (apiVersionDescription.IsDeprecated)
{
document.Description += " This API version has been deprecated.";
}
document.PostProcess = doc =>
{
doc.Info.Contact = new()
{
Name = settings.ContactName,
Email = settings.ContactEmail,
Url = settings.ContactUrl
};
doc.Info.License = new()
{
Name = settings.LicenseName,
Url = settings.LicenseUrl
};
};
document.AddSecurity(JwtBearerDefaults.AuthenticationScheme, new OpenApiSecurityScheme
{
Name = "Authorization",
Description = "Input your Bearer token to access this API",
In = OpenApiSecurityApiKeyLocation.Header,
Type = OpenApiSecuritySchemeType.Http,
Scheme = JwtBearerDefaults.AuthenticationScheme,
BearerFormat = "JWT",
});
document.OperationProcessors.Add(new AspNetCoreOperationSecurityScopeProcessor());
document.OperationProcessors.Add(new SwaggerGlobalAuthProcessor());
document.TypeMappers.Add(new PrimitiveTypeMapper(typeof(TimeSpan), schema =>
{
schema.Type = NJsonSchema.JsonObjectType.String;
schema.IsNullableRaw = true;
schema.Pattern = @"^([0-9]{1}|(?:0[0-9]|1[0-9]|2[0-3])+):([0-5]?[0-9])(?::([0-5]?[0-9])(?:.(\d{1,9}))?)?$";
schema.Example = "02:00:00";
}));
document.OperationProcessors.Add(new SwaggerHeaderAttributeProcessor());
var fluentValidationSchemaProcessor = serviceProvider.CreateScope().ServiceProvider.GetService<FluentValidationSchemaProcessor>();
document.SchemaProcessors.Add(fluentValidationSchemaProcessor);
});
} app.UseOpenApi(settings =>
{
});
app.UseSwaggerUi3(settings =>
{
settings.DefaultModelsExpandDepth = -1;
settings.DocExpansion = "none";
settings.TagsSorter = "alpha";
}); |
document.DocumentName = apiVersionDescription.ApiVersion.ToString("'v'V");
document.ApiGroupNames = new[] { document.DocumentName };
document.Version = apiVersionDescription.ApiVersion.ToString("VV"); Custom GroupingRepeating it here would be verbose, but there are a couple of options for custom grouping to achieve the results you want. The simplest way is likely using your own |
Should solve lots of api versioning problems, see #1355
This is a breaking change for users of the old ApiVersionProcessor: Use AddVersionedApiExplorer() in ConfigureServices() and define ApiGroupNames instead of IncludedVersions
Without UseDocumentProvider:
With UseDocumentProvider:
Sample project: https://github.com/RSuter/NSwag/tree/master/src/NSwag.SwaggerGeneration.AspNetCore.Tests.Web