AspNetCoreOData
AspNetCoreOData copied to clipboard
Using odata 8 with AddVersionedApiExplorer braking the swagger page.
In my configure services if i add these services.AddVersionedApiExplorer();
it will break the swagger and saw us swagger with
'No operations defined in spec!'
if i comment out this code it works as expected i am not sure why. any ideas My configure services
public void ConfigureServices(IServiceCollection services)
{
services.AddVersionedApiExplorer();
services.AddControllers()
.AddOData(opt => opt.Count().Filter().Expand().Select().OrderBy().SetMaxTop(100)
.AddRouteComponents("api/management/v2", EdmModelBuilder.GetEdmModelV2())
.Conventions.Add(new CustomConvention())
//.Conventions.Remove(opt.Conventions.First(convention => convention is MetadataRoutingConvention))
);
services.AddSwaggerGen(swaggerOptions =>
{
swaggerOptions.SwaggerDoc("v2",
new OpenApiInfo
{
Title = "test Management",
Description = "This is the Swagger documentation for the test Management API.",
Version = Assembly.GetEntryAssembly().GetName().Version.ToString()
});
swaggerOptions.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
{
Name = "Authorization",
In = ParameterLocation.Header,
Type = SecuritySchemeType.Http,
Scheme = "Bearer"
});
swaggerOptions.AddSecurityRequirement(new OpenApiSecurityRequirement()
{
{
new OpenApiSecurityScheme
{
Reference = new OpenApiReference
{
Type = ReferenceType.SecurityScheme,
Id = "Bearer"
},
Scheme = "oauth2",
Name = "Bearer",
In = ParameterLocation.Header
},
new List<string>()
}
});
swaggerOptions.DocumentFilter<SwaggerDocumentFilter>();
swaggerOptions.IncludeXmlComments(Path.Combine(AppContext.BaseDirectory, $"{Assembly.GetExecutingAssembly().GetName().Name}.xml"));
});
services.AddControllers(options =>
{
IEnumerable<ODataOutputFormatter> outputFormatters =
options.OutputFormatters.OfType<ODataOutputFormatter>()
.Where(formatter => !formatter.SupportedMediaTypes.Any());
foreach (var outputFormatter in outputFormatters)
{
outputFormatter.SupportedMediaTypes.Add(new MediaTypeHeaderValue("application/odata"));
}
IEnumerable<ODataInputFormatter> inputFormatters =
options.InputFormatters.OfType<ODataInputFormatter>()
.Where(formatter => !formatter.SupportedMediaTypes.Any());
foreach (var inputFormatter in inputFormatters)
{
inputFormatter.SupportedMediaTypes.Add(new MediaTypeHeaderValue("application/odata"));
}
});
AddFormatters(services);
}
Configure
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseSwagger(swaggerOptions =>
{
swaggerOptions.PreSerializeFilters.Add
(
(swagger, httpReq) =>
{
swagger.Servers = new List<OpenApiServer>
{
new OpenApiServer { Url = $"{httpReq.Scheme}://{httpReq.Host.Value}" }
};
}
);
});
app.UseSwaggerUI(swaggerUIOptions =>
{
swaggerUIOptions.SwaggerEndpoint($"/swagger/v2/swagger.json", $"CM v2");
});
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}```
Controller
[ApiVersion("2.0")] public class FoldersController : BaseController {}
if you send http://localhost:5000/swagger/swagger-ui-bundle.js
, you can get a js file.
Search 'No operations defined in spec', you can see it in the js file.
can you explain it little bit more about how we can fix this? I see this file but what do we need to do after that?
@jineshpatel99 Actually, i haven't figured the 'solution' out. did you file the same issue to swagger UI and get any feedback from them?
Just did
@jineshpatel99 please add a link to the issue on the other repo for traceability.
https://github.com/swagger-api/swagger-ui/issues/7868 Link to swagger ui issue.
@jineshpatel99 this is strange scenario and setup indeed. You're using API Versioning with API Explorer support, but only the intrinsic OData setup. OData 8 is not currently supported in API Versioning, but it's finally almost here (see dotnet/aspnet-api-versioning#677). The reason this is happening is because the API Versioning extensions for API Explorer group ApiDescription
results by their ApiVersion
. Results that are not grouped are ultimately discarded. Since the OData endpoints will not have a corresponding group or API version, they are thrown out.
Versioned, vanilla endpoint mixed with unversioned OData endpoints combined with API Explorer feels like a bizarre setup to me. However, in the spirit of POLA, I will concede that this is not something you expect. I think it's fair API Versioning's API Explorer extensions shouldn't blindly remove results that it doesn't know what to do with.
There are at least 2 possible solutions.
Solution 1
Extend the VersionedApiDescriptionProvider
class and override ShouldExploreAction
. If the action is for OData and the ApiVersion
matches what you expect, then return true and it will be included in the results.
You'll also need to replace the default registration of IApiDescriptionProvider
for VersionedApiDescriptionProvider
with your replacement implementation in the IServiceCollection
.
Solution 2
Add a custom IApiDescriptionProvider
that sits in front of the API Versioning extensions, captures the OData endpoints and re-adds them.
Something like:
public class MyODataApiDescriptionProvider : IApiDescriptionProvider
{
private readonly List<ApiDescription> results = new();
public int Order => -100; // run before API Versioning
public void OnProvidersExecuting(ApiDescriptionProviderContext context)
{
results.Clear();
for (var i = 0; i < context.Results; i++)
{
var result = results[i];
if (result.ActionDescriptor == /* OData */)
{
results.Add(result);
}
}
}
public void OnProvidersExecuted(ApiDescriptionProviderContext context)
{
for (var i = 0; i < results.Count; i++)
{
context.Results.Add(results[i]);
}
}
}
Both of these are reasonable solutions, but if you feel this should be intrinsically supported, feel free to open an issue in the API Versioning repo.
Sidebar: @xuzhg incorporated my solution and fixed the formatters in the OData 8+ release so that you don't have to remove or update them anymore.
@commonsensesoftware Thank you very much, the second solution works for us.
Quick update. API Versioning 6.0 is currently available in preview with support for .NET 6.0, OData 8.0, and the API Explorer extensions which can be used for OpenAPI. This bug has also been fixed. If you want to use the API Explorer for non-OData routes only, you can; however, both OData and non-OData endpoints are now fully supported.
Note that there are new packages in 6.0
+. You are likely interested in:
Referencing just Asp.Versioning.OData.ApiExplorer will bring in everything. If you really only want API Explorer for non-OData routes, then you'll want:
Also note that the setup now looks like:
services.AddApiVersioning() // Core API Versioning services with support for Minimal APIs
.AddMvc() // API versioning extensions for MVC Core
.AddApiExplorer() // API version-aware API Explorer extensions
.AddOData() // API versioning extensions for OData
.AddODataApiExplorer(); // API version-aware API Explorer extensions for OData
I have setup following
services.AddControllers()
.AddOData((opt) =>
{
opt.EnableQueryFeatures()
.AddRouteComponents("api/V1", GetEdmModelV1())
.AddRouteComponents("api/V2", GetEdmModelV2());
});
and then
services.AddApiVersioning() // Core API Versioning services with support for Minimal APIs
.AddMvc() // API versioning extensions for MVC Core
.AddApiExplorer() // API version-aware API Explorer extensions
.AddOData() // API versioning extensions for OData
.AddODataApiExplorer();
Adding Odata in Versioning breaks the ability to perform select, filter etc. and Removing that breaks the swagger versioning. Any clue how to resolve this?
#1096
@akshaybheda Your setup is incorrect. I've provided the correct setup in #1096.