Aspnet-api-versioning: swagger not working with referenced assembly - odata controllers

Created on 22 Mar 2019  路  18Comments  路  Source: microsoft/aspnet-api-versioning

I have a referenced assembly which has a group of controllers and an implementation of the IModelConfiguration which works with the /api/$metadata and odata URIs; however these controllers/models are not documented/displayed when using the OData Versioning API/Swagger documentation?

Please advise on how referenced assemblies may be documented/included in the swagger documentation of the odata endpoint?

answered odata question

All 18 comments

You didn't specify which platform, but I'm _guessing_ that you are referring to ASP.NET Core. If you have IModelConfiguration implementations in a separate assembly, the issue is that they are not being automatically detected by dependency injection.

There are 3 possible solutions:

  1. Add your assembly or assemblies to the ApplicationPartManager
  2. Explicitly register your model configurations as services (e.g. services.TryAddEnumerable(ServiceDescriptor.Transient<IModelConfiguration, MyModelConfig>()))
  3. Explicitly add your model configurations before calling VersionedODataModelBuilder.GetEdmModels() (e.g. modelBuilder.ModelConfigurations.Add(new MyModelConfig()))

Any of those should work. These behaviors are combined with the default dependency injection behavior so make sure that you only add model configurations that haven't been already been added. If your controllers aren't been discovered, this is more systemic to how ASP.NET Core works, but it should be solved via #1 by adding the assemblies containing the controllers to the ApplicationPartManager. If you're already doing that, then the issue is likely a sequencing problem in your setup.

@commonsensesoftware sorry, yes I am using .netcore; the IModelConfiguration is resolved and the Apply method is getting called as expected; and the api/$metadata is also showing the model as as configured in the referenced assembly. However the swagger endpoint fails to show any of the odata controller?

Was it working without API Versioning? It sounds like ASP.NET Core is not discovering your controllers. If you're able to share the relevant part of your startup, it might help.

@commonsensesoftware I can confirm that everything works as expected with regards to accessing controller when in a referenced assembly, I also confirm that copying code into the same web api host project without change works. I'm using the SwaggerOdata sample from this repo as an example

it looks like something is not getting configured in the DI system when using versioning from a reference assembly;

> fail: Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware[1]
>       An unhandled exception has occurred while executing the request.
> System.InvalidOperationException: No media types found in 'Microsoft.AspNet.OData.Formatter.ODataOutputFormatter.SupportedMediaTypes'. Add at least one media type to the list of supported media types.
>    at Microsoft.AspNetCore.Mvc.Formatters.OutputFormatter.GetSupportedContentTypes(String contentType, Type objectType)
>    at Microsoft.AspNetCore.Mvc.ApiExplorer.ApiResponseTypeProvider.CalculateResponseFormats(ICollection`1 responseTypes, MediaTypeCollection declaredContentTypes)
>    at Microsoft.AspNetCore.Mvc.ApiExplorer.ApiResponseTypeProvider.GetApiResponseTypes(IReadOnlyList`1 responseMetadataAttributes, Type type, Type defaultErrorType)
>    at Microsoft.AspNetCore.Mvc.ApiExplorer.ApiResponseTypeProvider.GetApiResponseTypes(ControllerActionDescriptor action)
>    at Microsoft.AspNetCore.Mvc.ApiExplorer.DefaultApiDescriptionProvider.CreateApiDescription(ControllerActionDescriptor action, String httpMethod, String groupName)
>    at Microsoft.AspNetCore.Mvc.ApiExplorer.DefaultApiDescriptionProvider.OnProvidersExecuting(ApiDescriptionProviderContext context)
>    at Microsoft.AspNetCore.Mvc.ApiExplorer.ApiDescriptionGroupCollectionProvider.GetCollection(ActionDescriptorCollection actionDescriptors)
>    at Microsoft.AspNetCore.Mvc.ApiExplorer.ApiDescriptionGroupCollectionProvider.get_ApiDescriptionGroups()
>    at Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenerator.GetSwagger(String documentName, String host, String basePath, String[] schemes)
>    at Swashbuckle.AspNetCore.Swagger.SwaggerMiddleware.Invoke(HttpContext httpContext, ISwaggerProvider swaggerProvider)
>    at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)

NOTE: DI is populated from using an ApplicationPart which is has the following

>             services.AddApiVersioning( options => options.ReportApiVersions = true );
>             services.AddOData( ).EnableApiVersioning();
>             services.AddODataApiExplorer(options =>
>             {
>                 // add the versioned api explorer, which also adds IApiVersionDescriptionProvider service
>                 // note: the specified format code will format the version as "'v'major[.minor][-status]"
>                 // options.GroupNameFormat = "'v'VVV";
>                
>                 // note: this option is only necessary when versioning by url segment. the SubstitutionFormat
>                 // can also be used to control the format of the API version in route templates
>                 options.SubstituteApiVersionInUrl = true;
>             });
> 
>             services.AddTransient<IConfigureOptions<SwaggerGenOptions>, ConfigurationOptions>();
>             services.AddSwaggerGen(
>                 options =>
>                 {
>                     options.OperationFilter<DefaultValues>();
>                     options.AddSecurityDefinition("Bearer", 
>                         new ApiKeyScheme()
>                         {
>                             Name = "Authorization", In = "header", Type = "apiKey",
>                             Description =
>                                             "A access token (Json Web Token) using the authorization header using the 'Bearer' scheme. " +
>                                             "Example: \"Authorization: Bearer {token}\"",
>                         });
>                 });

Do you happen to be mixing OData and non-OData controllers? I suspect that you might be. This is a known issue in the OData library. I filed bug OData/WebApi#1750, which also contains a possible workaround. API Versioning has special handling for this, but until I dug deeper, I could never figure out why this was happening.

While I could _hack_ around it, it doesn't make sense for me to do that. Once I introduce a workaround, I'll have to own it. Fortunately, if you're in this situation, it's not a lot of work for you to get around.

@commonsensesoftware yes, we have a group of hosts with a number of (plug-able modules) that have a selection of non-odata api controllers for access/session token management etc.; these also have a number actions etc. in the same host under the odata endpoint which are derived from the ODataControllers?

Thanx for the heads up with regards the use case, seems odd that the issue only appears for me when using controllers in reference assembly?

Oh .. this issue isn't specific to reference assemblies. It will happen any time you mix OData and non-OData controllers, even in the same assembly/application. In the bug, I put links that will take you to right to where the problem happens. I'd submit a PR to fix it, but their PR acceptance rate makes me not want to put forth the effort, even though it's minimal effort for me. Maybe one of those days when I have all that _free_ time. =P

The workaround should work for you. If you don't care about OData's specific dynamical content negotiation feature (which isn't all that common), then you can remove that formatter and all should be good.

@commonsensesoftware I'm not seeing the ODataInputFormatter or ODataOutputFormatter within the MvcOptions ? any additional pointers ?

@commonsensesoftware there looks to be another issue with regards the generation of swagger documentation for controllers the are derived from ODataControllers which for whatever reason don't seem to be getting resolved when in a referenced assembly and therefore not getting including in the swagger output?

You should see the ODataInputFormatter and ODataOutputFormatter in the MvcOptions.InputFormatters and MvcOptions.OutputFormatters respectively. This is added by OData via services.AddOData. To make sure you hook into the configuration after this is done, the best approach might be to create and register your own IPostConfigureOptions<MvcOptions> which will be called back after all options have been configured. At this point, OData should have added the input and output formatter and you remove or modify then as needed.

Can you confirm whether the API descriptions returned by the API explorer contain (or don't contain) your controllers. I guessing probably not since Swagger generates like Swashbuckle and NSwag rely completely on that service to do their work. However, if they are there, then something else is amiss.

If you're able to boil things down to the simplest repro, it would be immensely useful. Aside from it being time consuming for me to recreate your setup, there's the additional chance that I don't set it up the same way you have. If I have something to work backward from, it usual accelerates the process of tracking down the issue.

@commonsensesoftware I'm going to start a fresh with a very simple project that shows the issue I'm hitting

@grahamehorner were you able to make progress on this or resolve the issue? Thanks.

It seems you ultimately resolved this issue or may have abandoned your approach. If you have a repro project to look at, I'm happy to assist. Feel free to come back and/or reopen the issue if you are still facing issues. Thanks.

Why was this closed? AddOData still causes a crash on Microsoft.AspNetCore.Mvc.ApiExplorer.ApiResponseTypeProvider.CalculateResponseFormats

" No media types found in 'Microsoft.AspNet.OData.Formatter.ODataOutputFormatter.SupportedMediaTypes'. Add at least one media type to the list of supported media types."

@onionhammer the issue had run its course. I've provided all the guidance and workarounds I know. If the author of an issue doesn't close an issue out themselves, I generally ping them when the issue seems resolved and afford a week for response. After that, I typically close out issues. I run this repo by myself and I don't have the capacity to manage hundreds or thousands of open issues.

It would seem you are mixing OData and non-OData controllers. The systemic issue is in the OData libraries. I can't fix that. I filed a bug on the OData repo. If I had confidence that they'd accept it, I'd submit a PR, but their merge rate is historically low and slow. If I had more capacity, I'd consider it. The fix is trivial and I've outlined exactly what they need to do, but they've continued to not patch it. If this is causing you grief, then I would suggest you follow up on issue linked above and ask why they still haven't fixed it (it's been more than a year).

If there's some other issue specifically related to API versioning, I'm happy to help.

Was this page helpful?
0 / 5 - 0 ratings