Skip to content

Unable to integrate Odata versioned apis with swagger for Asp.net web application #302

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

Closed
vanikulkarniAtFIS opened this issue May 23, 2018 · 16 comments
Assignees

Comments

@vanikulkarniAtFIS
Copy link

vanikulkarniAtFIS commented May 23, 2018

We are using VersionedODataModelBuilder in Http application ( Asp.net web application)
Its does not have Owin startup hence we pass GlobalConfiguration.Configuration to VersionedODataModelBuilder constructor
Versiong of OData uri works fine as shown in the SwaggerODataWebApiSample
But while integrating this with swagger gives problems:

we create exploere as

var apiExplorer = configuration.AddODataApiExplorer(
options =>
{
options.GroupNameFormat = "'v'VVV";
options.SubstituteApiVersionInUrl = true;
});

The exception comes at
foreach (var group in apiExplorer.ApiDescriptions)
here apiExplorer.ApiDescriptions throws excpetion Invalid Operation exception
HttpConfiguration.Services.GetApiExplorer "This operation is not supported by 'HostedHttpRouteCollection'."
apiExplorer.ApiDescriptions: This operation is not supported by 'HostedHttpRouteCollection'.

The reasons could be : GlobalConfiguration.Configuration.Routes is a collection of HostedHttpRoutes
whereas in sample application we create new HttpConfigutation
Routes = {System.Web.Http.HttpRouteCollection}
vs
Routes = {System.Web.Http.WebHost.Routing.HostedHttpRouteCollection}
We also have normal ( non-odata) web apis as well

Swagger documentation is configured for them too.
Do you have any such example where odata versioning integrated with swagger and its not Owin

@commonsensesoftware
Copy link
Collaborator

I haven't seen this issue nor had anyone else report it. Using OWIN shouldn't matter, at least, not in this context. I mostly use OWIN for examples so that the Web API and ASP.NET Core examples have close parity.

I can't rule out a bug - yet. My cursory examination suggests that this might be an external. Are you by chance modifying the route collection in any way? Do you happen to have a repro handy? I can certainly create one, but working from something simple you already have is faster and an par with your expected setup.

@vanikulkarniAtFIS
Copy link
Author

vanikulkarniAtFIS commented May 24, 2018

Chris,
Thanks for your quick reply.
At the moment I cannot upload the source code as its against security policy,
There no way to modify route collection as its read only property
What could be the external factors you think is affecting this?
We are using

config.Services.Replace(typeof(IApiExplorer), new IntegrationServicesApiExplorer());
IntegrationServicesApiExplorer is searching for controllers in the IntegrationServices folder.
My OData Controllers and normal web api controllers lie in the same folder
I don't think this should affect it.

I have attached sample code the comment below.
Please suggest some fix/ workaround for this.

@vanikulkarniAtFIS
Copy link
Author

Swashbuckle.OData.Sample.zip

Please find attached sample code where the issue is reproduced
We are facing this issue

System.NotSupportedException was unhandled by user code
HResult=-2146233067
Message=This operation is not supported by 'HostedHttpRouteCollection'.
Source=System.Web.Http.WebHost
StackTrace:
at System.Web.Http.WebHost.Routing.HostedHttpRouteCollection.CopyTo(KeyValuePair2[] array, Int32 arrayIndex) at System.Web.Http.HttpRouteCollectionExtensions.GetRouteName(HttpRouteCollection routes, IHttpRoute route) at System.Web.Http.HttpConfigurationExtensions.GetODataRootContainer(HttpConfiguration configuration, IHttpRoute route) at Microsoft.Web.Http.Description.ODataApiExplorer.ExploreRouteControllers(IDictionary2 controllerMappings, IHttpRoute route, ApiVersion apiVersion)
at Microsoft.Web.Http.Description.VersionedApiExplorer.InitializeApiDescriptions()
at System.Lazy1.CreateValue() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() at System.Lazy1.get_Value()
at Microsoft.Web.Http.Description.VersionedApiExplorer.get_ApiDescriptions()
InnerException:

@vanikulkarniAtFIS
Copy link
Author

vanikulkarniAtFIS commented May 25, 2018 via email

@vanikulkarniAtFIS
Copy link
Author

vanikulkarniAtFIS commented May 30, 2018 via email

@commonsensesoftware
Copy link
Collaborator

commonsensesoftware commented Jun 6, 2018

Sorry for the delayed response, I've been really overloaded lately. Thanks for your patience. I tried downloading your sample application, but it seems it's no longer there. I don't think GitHub expires this content. Can you try adding it again?

I noticed you said you're using:

config.Services.Replace(typeof(IApiExplorer), new IntegrationServicesApiExplorer());

Is IntegrationServicesApiExplorer an extension of ODataApiExplorer? If it's not, then I expect this to fail. There is little-to-no support for OData and the IApiExplorer out-of-the-box. I provide a separate package and library to support that. It's normally registered and used as:

var apiExplorer = config.AddODataApiExplorer();

This ultimately calls config.Services.Replace(typeof(IApiExplorer), new ODataApiExplorer(config, options)). If you want to extend and provide your own customizations, that is a supported scenario. You'll just need to do the registration manually as you're currently doing. If you find a gap or limitation in customizing the current ODataApiExplorer implementation, I'm happy to discuss the changes needed to unblock you.

@vanikulkarniAtFIS
Copy link
Author

vanikulkarniAtFIS commented Jun 7, 2018

Thanks for your reply. In tha sample application we are not replacing IApiExplorer. Attaching the zip file again. Let me know if you can see this

Swashbuckle.OData.Sample.zip

@commonsensesoftware
Copy link
Collaborator

I think I see the problem. You're replacing the IHttpControllerSelector, but I don't know why. That's likely going to break API versioning, the API Explorer, or both. It appears that you started trying to build your own API versioning library and then switched.

The IHttpControllerSelector is one of the core services that API versioning changes. You can see the implementation here. You can certainly extend and/or customize things, but if you go down that path, you will not want to use AddApiVersioning. Instead, you'll want to register the services manually. From a routing perspective, there is no difference to API versioning between OData and non-OData routes; that's why they don't have different configuration methods. The things that change for OData is versioning the EDM and adding version-aware OData route conventions.

I've provided a number of OData samples - with and without Swagger. I think you're facing issues from customizations that may no longer be needed (on your side). If there is some customization or configuration you need that isn't supported, I'm happy to help you sort that out. If there's gap in design, then I want to hear about that too.

Thanks

@commonsensesoftware
Copy link
Collaborator

commonsensesoftware commented Jun 7, 2018

Yureka! I see what the problem is. You are trying to follow and use code from the Swashbuckle + OData project. I just noticed something that caught my eye and made me double-check.

That project is completely different, unrelated, and not compatible with the API versioning libraries.

This is the first time I've seen this confusion, but I should probably mention something about that on the wiki. This project provides an implementation for IApiExplorer. It actually has no dependencies on Swashbuckle or Swagger in any way. Swashbuckle uses the IApiExplorer for the Web API implementation to support Swagger.

For an end-to-end working solution with API versioning, OData, and Swagger (a la Swashbuckle), please review the sample I provide. You should be able fork it and get it running for your project in no time.

I hope that helps. I'm happy to answer any other questions.

@vanikulkarniAtFIS
Copy link
Author

vanikulkarniAtFIS commented Jun 8, 2018

Thanks for quick reply.
We had tried this approach(similar to your sample app) earlier as well and that did not work for us due to hosted routes collection.
We have removed OWIN startup file from your application to replicate scenario as that of our application
Please see attached to reproduce the issue .
SwaggerODataWebApiSample.zip
the issue

@commonsensesoftware
Copy link
Collaborator

I was able to repro the problem. Honestly, this is a bug and flaw in the way that the Web API support over IIS was written, but it's unlikely to get fixed any time soon - if ever. The issue is that RouteCollection in System.Web.dll doesn't expose the names of routes that it registers. The HostedHttpRouteCollection that maps over RouteCollection could have mapped these keys during add/remove to the underlying RouteCollection, but didn't. Instead, it just throw NotSupportedException. These types of runtime integration failures are difficult to detect and/or account for.

I have the fix for this, but it involves very nasty Reflection under the hood. It looks like this scenario is only hit for OData with Web API and hosted on IIS. As much as I wish I could test every permutation of hosting combinations, I'm only one person. Regardless, the fix is relatively small. I should have something out momentarily.

@commonsensesoftware
Copy link
Collaborator

Version 1.2.2 of the package with the fix has been published. It may take a few minutes to get through the full NuGet vetting process. Feel free to re-open this, if you continue to have an issue. Thanks.

@vanikulkarniAtFIS
Copy link
Author

Thanks a ton Chris for your efforts and making this so quick. you have saved us from implementing workaround for versioning on OData apis.

@vanikulkarniAtFIS
Copy link
Author

vanikulkarniAtFIS commented Jun 14, 2018

We are facing some issue. The properties of json are in camel case whereas it should have been the way they are defined in the entity.
By default the name of api controller also appears in camel case
The sample application (SwaggerOdataWebApiSample) create by you also has this issue.
Any pointers how to resolve this?
Entity :
public class Assessment
{
public int Id { get; set; }
public string AssessmentName { get; set; }
public long? AssessmentIdentifier { get; set; }
}

We did following setting for Default PropertyNames Convension still it displays Entity Propertis in Camel Case in API responce.
//httpConfiguration.Formatters.JsonFormatter.SerializerSettings.ContractResolver = new DefaultContractResolver();
API Response structure:
{
Id :123
assessmentName:"Test"
assessmentIdentifier:"12345"
}

Issue No 2:

Package Required : Swashbuckle.OData

We are using Swashbuckle.OData package to display Odata Query option on Swagger document. Following configuration is done.

configuration.EnableSwagger(SwaggerDocsRoute, swagger =>
{
swagger.CustomProvider(defaultProvider => new ODataSwaggerProvider(defaultProvider, swagger,configuration).Configure(odataConfig =>
{
odataConfig.IncludeNavigationProperties();
}));
}).EnableSwaggerUi(SwaggerUiRoute, swagger => swagger.EnableDiscoveryUrlSelector());

After Odata Versiong ODataSwaggerProvider throws Exception -
Additional information: Value cannot be null.
{"Value cannot be null.\r\nParameter name: routeData"}
at System.Net.Http.HttpRequestMessageExtensions.SetRouteData(HttpRequestMessage request, IHttpRouteData routeData)
at Swashbuckle.OData.Descriptions.SwaggerRouteStrategy.CreateHttpRequestMessage(HttpMethod httpMethod, Operation potentialOperation, SwaggerRoute potentialSwaggerRoute, HttpConfiguration httpConfig) in C:\Users\rbeauchamp\Documents\GitHub\Swashbuckle.OData\Swashbuckle.OData\Descriptions\SwaggerRouteStrategy.cs:line 105
at Swashbuckle.OData.Descriptions.SwaggerRouteStrategy.GetActionDescriptors(HttpMethod httpMethod, Operation potentialOperation, SwaggerRoute potentialSwaggerRoute, HttpConfiguration httpConfig) in C:\Users\rbeauchamp\Documents\GitHub\Swashbuckle.OData\Swashbuckle.OData\Descriptions\SwaggerRouteStrategy.cs:line 67
at Swashbuckle.OData.Descriptions.SwaggerRouteStrategy.GetActionDescriptors(SwaggerRoute potentialSwaggerRoute, HttpConfiguration httpConfig) in C:\Users\rbeauchamp\Documents\GitHub\Swashbuckle.OData\Swashbuckle.OData\Descriptions\SwaggerRouteStrategy.cs:line 52
at Swashbuckle.OData.Descriptions.SwaggerRouteStrategy.<>c__DisplayClass3_0.b__1(SwaggerRoute potentialSwaggerRoute) in C:\Users\rbeauchamp\Documents\GitHub\Swashbuckle.OData\Swashbuckle.OData\Descriptions\SwaggerRouteStrategy.cs:line 41
at System.Linq.Enumerable.d__162.MoveNext() at System.Linq.Enumerable.<SelectManyIterator>d__162.MoveNext()
at System.Linq.Enumerable.d__631.MoveNext() at System.Linq.Enumerable.<SelectManyIterator>d__162.MoveNext()
at System.Collections.Generic.List1..ctor(IEnumerable1 collection)
at System.Linq.Enumerable.ToList[TSource](IEnumerable1 source) at Swashbuckle.OData.CollectionExtentions.ToCollection[T](IEnumerable1 source) in C:\Users\rbeauchamp\Documents\GitHub\Swashbuckle.OData\Swashbuckle.OData\CollectionExtentions.cs:line 77
at Swashbuckle.OData.Descriptions.ODataApiExplorer.GetApiDescriptions() in C:\Users\rbeauchamp\Documents\GitHub\Swashbuckle.OData\Swashbuckle.OData\Descriptions\ODataApiExplorer.cs:line 43
at System.Lazy`1.CreateValue()

Issue No -3
Compile time error is occured for Odata ApiExplorer.
{"Object reference not set to an instance of an object."}
at Microsoft.Web.Http.Description.TypeExtensions.<>c__DisplayClass10_0.b__0(IEdmStructuredType t)
at System.Linq.Enumerable.FirstOrDefault[TSource](IEnumerable1 source, Func2 predicate)
at Microsoft.Web.Http.Description.TypeExtensions.GetStructuredType(Type type, IEdmModel model, IAssembliesResolver assembliesResolver)
at Microsoft.Web.Http.Description.TypeExtensions.SubstituteIfNecessary(Type type, IServiceProvider serviceProvider, IAssembliesResolver assembliesResolver, ModelTypeBuilder modelTypeBuilder)
at Microsoft.Web.Http.Description.ODataApiExplorer.CreateParameterDescriptionFromBinding(HttpParameterBinding parameterBinding, IServiceProvider serviceProvider, IAssembliesResolver assembliesResolver)
at Microsoft.Web.Http.Description.ODataApiExplorer.CreateParameterDescriptions(HttpActionDescriptor actionDescriptor, IHttpRoute route)
at Microsoft.Web.Http.Description.ODataApiExplorer.ExploreRouteActions(IHttpRoute route, HttpControllerDescriptor controllerDescriptor, IHttpActionSelector actionSelector, Collection1 apiDescriptions, ApiVersion apiVersion) at Microsoft.Web.Http.Description.ODataApiExplorer.ExploreRouteControllers(IDictionary2 controllerMappings, IHttpRoute route, ApiVersion apiVersion)
at Microsoft.Web.Http.Description.VersionedApiExplorer.InitializeApiDescriptions()
at System.Lazy1.CreateValue() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() at System.Lazy1.get_Value()
at Microsoft.Web.Http.Description.VersionedApiExplorer.get_ApiDescriptions()

@vanikulkarniAtFIS
Copy link
Author

There are 3 new issues we found, let me know if I need to create new issue

@commonsensesoftware
Copy link
Collaborator

Issue 1

OData doesn't use the default JSON MediaTypeFormatter; it uses its own. Camelcase is very common for JSON. I suspect you are using the configuration in the examples I provide, which enable this. Simply remove this configuration. For example:

var modelBuilder = new VersionedODataModelBuilder( configuration )
{
    // configuration shown in example using camelcase
    // ModelBuilderFactory = () => new ODataConventionModelBuilder().EnableLowerCamelCase(),

    // default configuration using source case, which usually Pascalcase
    ModelBuilderFactory = () => new ODataConventionModelBuilder(),

    // remaining configuration ...
};

Issue 2

I really know very little about the Swashbuckle.OData library or project. I don't use it or support it. You should have everything you need with the OData API versioning, OData API Explorer, and vanilla Swashbuckle. If there is something not supported that you need, I'd be interested in hearing about it to determine if a new feature is required. My recommendation would be to remove Swashbuckle.OData all together. It shouldn't be needed.

Issue 3

I'm not sure what this is. Does it repro in the example that you previously provided? If you can provide a copy of the most version of that sample where the scenario repros, I'll take a look. It's possible there's a bug.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants