rbeauchamp / swashbuckle.odata Goto Github PK
View Code? Open in Web Editor NEWExtends Swashbuckle with OData v4 support!
License: Other
Extends Swashbuckle with OData v4 support!
License: Other
Currently if multiple OData routes are configured, an exception is thrown when getting the Swagger UI:
500 : {"Message":"An error has occurred.","ExceptionMessage":"Sequence contains more than one matching element","ExceptionType":"System.InvalidOperationException","StackTrace":" at System.Linq.Enumerable.SingleOrDefault[TSource](IEnumerable1 source, Func
2 predicate)\r\n at Swashbuckle.OData.ODataSwaggerProvider.GetSwagger(String rootUrl, String apiVersion)\r\n at Swashbuckle.Application.SwaggerDocsHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)\r\n at System.Net.Http.HttpMessageInvoker.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)\r\n at System.Web.Http.Dispatcher.HttpRoutingDispatcher.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)\r\n at System.Net.Http.DelegatingHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)\r\n at System.Web.Http.HttpServer.<>n__FabricatedMethod9(HttpRequestMessage , CancellationToken )\r\n at System.Web.Http.HttpServer.d__0.MoveNext()"} http://localhost:59758/swagger/docs/v1
This may be a little hard to explain but I will give it a shot.... I stumbled across this issue tonight and was totally confused as to why my EF context was not returning an entity I knew existed. It turns out, the way the Swagger-UI fields are handling the strings has some subtle differences.
These two signatures are actually returning the string differently:
public virtual async Task<IHttpActionResult> Put([FromODataUri] string key, [FromBody] T safetyEquipment)
{
// magic stuff
}
public virtual async Task<IHttpActionResult> Put(string key, [FromBody] T safetyEquipment)
{
// magic stuff
}
The string value that returned correctly was when using the [FromODataUri] version of the function.
500 : {"Message":"An error has occurred.","ExceptionMessage":"Segments with multiple key values must specify them in 'name=value' form.","ExceptionType":"Microsoft.OData.Core.ODataException","StackTrace":" at Microsoft.OData.Core.UriParser.Parsers.SegmentKeyHandler.CreateKeySegment(ODataPathSegment segment, KeySegment previousKeySegment, SegmentArgumentParser key, ODataUriResolver resolver)\r\n at Microsoft.OData.Core.UriParser.Parsers.SegmentKeyHandler.TryCreateKeySegmentFromParentheses(ODataPathSegment previous, KeySegment previousKeySegment, String parenthesisExpression, ODataPathSegment& keySegment, Boolean enableUriTemplateParsing, ODataUriResolver resolver)\r\n at Microsoft.OData.Core.UriParser.Parsers.ODataPathParser.TryBindKeyFromParentheses(String parenthesesSection)\r\n at Microsoft.OData.Core.UriParser.Parsers.ODataPathParser.TryCreateSegmentForNavigationSource(String identifier, String parenthesisExpression)\r\n at Microsoft.OData.Core.UriParser.Parsers.ODataPathParser.CreateFirstSegment(String segmentText)\r\n at Microsoft.OData.Core.UriParser.Parsers.ODataPathParser.ParsePath(ICollection1 segments)\r\n at Microsoft.OData.Core.UriParser.Parsers.ODataPathFactory.BindPath(ICollection
1 segments, ODataUriParserConfiguration configuration)\r\n at Microsoft.OData.Core.UriParser.ODataUriParser.ParsePathImplementation()\r\n at Microsoft.OData.Core.UriParser.ODataUriParser.Initialize()\r\n at Microsoft.OData.Core.UriParser.ODataUriParser.ParsePath()\r\n at System.Web.OData.Routing.DefaultODataPathHandler.Parse(IEdmModel model, String serviceRoot, String odataPath, ODataUriResolverSetttings resolverSettings, Boolean enableUriTemplateParsing)\r\n at System.Web.OData.Routing.DefaultODataPathHandler.Parse(IEdmModel model, String serviceRoot, String odataPath)\r\n at Swashbuckle.OData.ODataApiExplorer.GenerateSampleODataPath(ODataRoute oDataRoute, String sampleODataAbsoluteUri)\r\n at Swashbuckle.OData.ODataApiExplorer.GetApiDescription(HttpMethod httpMethod, ODataRoute oDataRoute, String potentialPathTemplate, Operation potentialOperation)\r\n at Swashbuckle.OData.ODataApiExplorer.GetApiDescriptions(ODataRoute oDataRoute, String potentialPathTemplate, PathItem potentialOperations)\r\n at Swashbuckle.OData.ODataApiExplorer.GetApiDescriptions(ODataRoute oDataRoute)\r\n at Swashbuckle.OData.ODataApiExplorer.GetApiDescriptions()\r\n at System.Lazy1.CreateValue()\r\n at System.Lazy
1.LazyInitValue()\r\n at System.Lazy`1.get_Value()\r\n at Swashbuckle.OData.ODataApiExplorer.get_ApiDescriptions()\r\n at Swashbuckle.OData.ODataSwaggerProvider.GetApiDescriptionsFor(String apiVersion)\r\n at Swashbuckle.OData.ODataSwaggerProvider.GetSwagger(String rootUrl, String apiVersion)\r\n at Swashbuckle.Application.SwaggerDocsHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)\r\n at System.Net.Http.HttpMessageInvoker.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)\r\n at System.Web.Http.Dispatcher.HttpRoutingDispatcher.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)\r\n at System.Net.Http.DelegatingHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)\r\n at System.Web.Http.HttpServer.<>n__FabricatedMethod9(HttpRequestMessage , CancellationToken )\r\n at System.Web.Http.HttpServer.d__0.MoveNext()"} http://localhost:57336/swagger/docs/v1
I have an API that combines both ApiController and ODataController in a single app. Adding the ODataSwaggerProvider properly documents the OData calls, but hides the documentation for the REST calls. Is this a known issue? Is there a workaround?
I am trying to keep separate in two different URL the docs for oData controllers and the docs for Api Controllers.
Is it doable?
Thank you
I have this scenary:
public class ToolHistory
{
[Key]
public int ID { get; set; }
public string Code { get; set; }
public string Name { get; set; }
public string Description { get; set; }
}
var builder = new ODataConventionModelBuilder()
builder.EntitySet<ToolHistory>("ToolHistories");
var model = builder.GetEdmModel();
var edmEntityType = model.FindType(namespace + "ToolHistory") as IEdmEntityType;
var edmProperty = edmEntityType.FindProperty("Code");
(model as EdmModel).AddAlternateKeyAnnotation(edmEntityType, new Dictionary<string, IEdmProperty>
{
{"Code", edmProperty}
});
This actions allow get item by key and by code.
public class ToolHistoriesController : ODataController
{
[EnableQuery]
public IHttpActionResult Get([FromODataUri] int key)
{
throw new NotImplementedException();
}
[EnableQuery]
[ODataRoute("ToolHistories(Code={code})")]
public IHttpActionResult Get([FromODataUri] string code)
{
throw new NotImplementedException();
}
}
If I add the [ODataRoute("ToolHistories(Code={code})")], the metadata is generate through AttributeRouteStrategy, but add all query parameters because I add EnableQuery attribute which is incorrect.I think that you should only add $expand and $select.
If I missing the odataroute attribute, the metadata for this action is not generated. There is another way for do this, maybe a SwaggerRoute Attribute?
attach a image
Please add the $filter parameter to the list of options in the UI.
The method GetODataActionDescriptorsFromAttributeRoutes in Swashbuckle.OData.Descriptions uses the Reflection Helper to get the field _attributeMappings form the Attribute Routing convention. If you create a custom implementation of AttributeRoutingConvention, for instance to override ShouldMapController this results in a error in the Reflection Helper. This because the fact that the field that is looked up could be a field of one of the base classes is not taken into account. This could be fixed by checking if the base class of the incoming object contains the requested field if the first lookup fails in the GetInstanceField method of the reflection helpers. I would be very grateful if this could be implemented.
I receive the following error when accessing the UI document. Please fix. Thank you.
500 : {"Message":"An error has occurred.","ExceptionMessage":"Bad Request - Error in query syntax.","ExceptionType":"Microsoft.OData.Core.ODataException","StackTrace":" at Microsoft.OData.Core.UriParser.Parsers.SegmentKeyHandler.TryCreateKeySegmentFromParentheses(ODataPathSegment previous, KeySegment previousKeySegment, String parenthesisExpression, ODataPathSegment& keySegment, Boolean enableUriTemplateParsing, ODataUriResolver resolver)\r\n at Microsoft.OData.Core.UriParser.Parsers.ODataPathParser.TryBindKeyFromParentheses(String parenthesesSection)\r\n at Microsoft.OData.Core.UriParser.Parsers.ODataPathParser.TryCreateSegmentForNavigationSource(String identifier, String parenthesisExpression)\r\n at Microsoft.OData.Core.UriParser.Parsers.ODataPathParser.CreateFirstSegment(String segmentText)\r\n at Microsoft.OData.Core.UriParser.Parsers.ODataPathParser.ParsePath(ICollection1 segments)\r\n at Microsoft.OData.Core.UriParser.Parsers.ODataPathFactory.BindPath(ICollection
1 segments, ODataUriParserConfiguration configuration)\r\n at Microsoft.OData.Core.UriParser.ODataUriParser.ParsePathImplementation()\r\n at Microsoft.OData.Core.UriParser.ODataUriParser.Initialize()\r\n at System.Web.OData.Routing.DefaultODataPathHandler.Parse(IEdmModel model, String serviceRoot, String odataPath, ODataUriResolverSetttings resolverSettings, Boolean enableUriTemplateParsing)\r\n at System.Web.OData.Routing.DefaultODataPathHandler.Parse(IEdmModel model, String serviceRoot, String odataPath)\r\n at Swashbuckle.OData.ODataApiExplorer.GenerateSampleODataPath(ODataRoute oDataRoute, String pathTemplate, Operation operation)\r\n at Swashbuckle.OData.ODataApiExplorer.GetApiDescription(HttpMethod httpMethod, ODataRoute oDataRoute, String potentialPathTemplate, Operation potentialOperation)\r\n at Swashbuckle.OData.ODataApiExplorer.GetApiDescriptions(ODataRoute oDataRoute, String potentialPathTemplate, PathItem potentialOperations)\r\n at Swashbuckle.OData.ODataApiExplorer.GetApiDescriptions(ODataRoute oDataRoute)\r\n at Swashbuckle.OData.ODataApiExplorer.GetApiDescriptions()\r\n at System.Lazy1.CreateValue()\r\n at System.Lazy
1.LazyInitValue()\r\n at System.Lazy`1.get_Value()\r\n at Swashbuckle.OData.ODataApiExplorer.get_ApiDescriptions()\r\n at Swashbuckle.OData.ODataSwaggerProvider.GetApiDescriptionsFor(String apiVersion)\r\n at Swashbuckle.OData.ODataSwaggerProvider.GetSwagger(String rootUrl, String apiVersion)\r\n at Swashbuckle.Application.SwaggerDocsHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)\r\n at System.Net.Http.HttpMessageInvoker.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)\r\n at System.Web.Http.Dispatcher.HttpRoutingDispatcher.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)\r\n at System.Net.Http.DelegatingHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)\r\n at System.Web.Http.HttpServer.d__0.MoveNext()"} http://localhost:59758/swagger/docs/v1
Child collections are not shown for ODataControllers.
In the example here: http://swashbuckleodata.azurewebsites.net/swagger/ui/index
The Customers ODataController resource does not show that in the GET, that Orders is available. It only shows primitive types (string, int etc).
The ApiController resource shows child resources (in the example Projects is a child collection of Client GET ApiController).
Can child collections be added to ODataControllers please?
Hi,
I am trying to install the new version of the nuget package to a .Net 4.5 application but I am getting the following error:
Could not install package 'Swashbuckle.OData 2.0.0'. You are trying to install this package into a project that targets '.NETFramework,Version=v4.5', but the package does not contain any assembly references or content files that are compatible with that framework. For more information, contact the package author.
The OData EDM should have all the navigation properties fully disclosed for introspection, so to expose the route to a navigation property should be automatic.
We have this route.
// GET: odata/AppLicenses(5)/Application
[EnableQuery]
public SingleResult<Application> GetApplication([FromODataUri] Guid key)
{
return SingleResult.Create(ConfigurationDataContext.AppLicenses.Where(m => m.AccountConfigurationID == key).Select(m => m.Application));
}
In order to get this otherwise fully functional OData route to appear in Swagger, I had to make the following change.
[EnableQuery]
[ODataRoute("AppLicenses({AccountConfigurationID})/Application")]
public SingleResult<Application> GetApplication([FromODataUri] Guid AccountConfigurationID)
{
return SingleResult.Create(ConfigurationDataContext.AppLicenses.Where(m => m.AccountConfigurationID == AccountConfigurationID).Select(m => m.Application));
}
It would be ideal if this didn't have to be treated as a custom route.
We've implemented some new routes using Containment and they are not documented correctly. Here is a sample project that will help reproduce the problem:
https://aspnet.codeplex.com/SourceControl/latest#Samples/WebApi/OData/v4/ODataContainmentSample/
Thanks for looking into this!
Given this example ..
[EnableQuery]
[ODataRoute("Foobars")]
public IQueryable<Foobar> GetFoobars(bool bar)
{
IEnumerable<Foobar> foobars = new[]
{
new Foobar { Id=1, Variation = "a"},
new Foobar { Id=2, Variation = "b"},
new Foobar { Id=3, Variation = "c"},
new Foobar { Id=4, Variation = "d"}
};
if (bar == true) foobars = foobars.Where(fb => fb.Id >= 3);
return foobars.AsQueryable();
}
.. if I change bool bar
to [FromODataUri] bool? bar = null
the parameter drops off completely from Swagger UI. I tried also updating [ODataRoute(..)]
with [ODataRoute("Foobars?bar={bar:bool?}")]
. I don't know if this is a missing feature (or bug) or if there's something I can do to make the parameter reappear. I'm not particularly concerned if, given whatever change I would make to get it to reappear, Swagger UI would still a true/false option here, I'd rather Swagger force an option than it not show the parameter at all, but I won't be able to make this parameter non-nullable outside of Swagger.
If I were to assume I'm doing it wrong, what am I missing? If it's a missing feature, any clue which class to get started poking at?
HI,
I tried to install the package, but get the following issue? Any idea of how to resolve it?
PM> Install-Package Swashbuckle.OData
Installing 'Swashbuckle.OData 2.0.0'.
Successfully installed 'Swashbuckle.OData 2.0.0'.
Adding 'Swashbuckle.OData 2.0.0' to Swagger.
Uninstalling 'Swashbuckle.OData 2.0.0'.
Successfully uninstalled 'Swashbuckle.OData 2.0.0'.
Install failed. Rolling back...
Install-Package : Could not install package 'Swashbuckle.OData 2.0.0'. You are
trying to install this package into a project that targets
'.NETFramework,Version=v4.5', but the package does not contain any assembly
references or content files that are compatible with that framework. For more
information, contact the package author.
At line:1 char:1
- CategoryInfo : NotSpecified: (:) [Install-Package], InvalidOper
ationException
- FullyQualifiedErrorId : NuGetCmdletUnhandledException,NuGet.PowerShell.C
ommands.InstallPackageCommand
I'll try to follow up with a dummy sample gist but I'm getting this error while the Swashbuckle.OData provider is enabled.
<Error><Message>An error has occurred.</Message><ExceptionMessage>Could not generate sample value for query parameter type array and format null</ExceptionMessage><ExceptionType>System.Exception</ExceptionType><StackTrace> at Swashbuckle.OData.ODataApiExplorer.GenerateSampleQueryParameterValue(Parameter queryParameter)
at System.Linq.Enumerable.ToDictionary[TSource,TKey,TElement](IEnumerable`1 source, Func`2 keySelector, Func`2 elementSelector, IEqualityComparer`1 comparer)
at System.Linq.Enumerable.ToDictionary[TSource,TKey,TElement](IEnumerable`1 source, Func`2 keySelector, Func`2 elementSelector)
at Swashbuckle.OData.ODataApiExplorer.GenerateSampleQueryParameterValues(Operation operation)
at Swashbuckle.OData.ODataApiExplorer.GenerateSampleODataAbsoluteUri(String pathTemplate, Operation operation)
at Swashbuckle.OData.ODataApiExplorer.GetApiDescription(HttpMethod httpMethod, ODataRoute oDataRoute, String potentialPathTemplate, Operation potentialOperation)
at Swashbuckle.OData.ODataApiExplorer.GetApiDescriptions(ODataRoute oDataRoute, String potentialPathTemplate, PathItem potentialOperations)
at Swashbuckle.OData.ODataApiExplorer.GetApiDescriptions(ODataRoute oDataRoute)
at Swashbuckle.OData.ODataApiExplorer.GetApiDescriptions()
at System.Lazy`1.CreateValue()
at System.Lazy`1.LazyInitValue()
at System.Lazy`1.get_Value()
at Swashbuckle.OData.ODataApiExplorer.get_ApiDescriptions()
at Swashbuckle.OData.ODataSwaggerProvider.GetApiDescriptionsFor(String apiVersion)
at Swashbuckle.OData.ODataSwaggerProvider.GetSwagger(String rootUrl, String apiVersion)
at Swashbuckle.Application.SwaggerDocsHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
at System.Net.Http.HttpMessageInvoker.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
at System.Web.Http.Dispatcher.HttpRoutingDispatcher.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
at System.Net.Http.DelegatingHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
at System.Web.Http.Cors.CorsMessageHandler.<>n__FabricatedMethod3(HttpRequestMessage , CancellationToken )
at System.Web.Http.Cors.CorsMessageHandler.<SendAsync>d__0.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
at System.Web.Http.Tracing.ITraceWriterExtensions.<TraceBeginEndAsyncCore>d__18`1.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
at System.Web.Http.Owin.PassiveAuthenticationMessageHandler.<SendAsync>d__0.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
at System.Web.Http.Tracing.ITraceWriterExtensions.<TraceBeginEndAsyncCore>d__18`1.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
at System.Web.Http.Tracing.ITraceWriterExtensions.<TraceBeginEndAsyncCore>d__18`1.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
at System.Web.Http.HttpServer.<SendAsync>d__0.MoveNext()</StackTrace></Error>
PM> Install-Package Swashbuckle.OData
Installing 'Swashbuckle.OData 2.10.1'.
Successfully installed 'Swashbuckle.OData 2.10.1'.
Adding 'Swashbuckle.OData 2.10.1' to GlobalCalendarWebApi.
Uninstalling 'Swashbuckle.OData 2.10.1'.
Successfully uninstalled 'Swashbuckle.OData 2.10.1'.
Install failed. Rolling back...
Install-Package : Could not install package 'Swashbuckle.OData 2.10.1'. You are trying to install this package into a project that targets '.NETFramework,Version=v4.5', but the package does not contain any assembly
references or content files that are compatible with that framework. For more information, contact the package author.
At line:1 char:16
Hi,
the use of the following line is hiding all the no primitive type property in the Swagger JSON.
c.CustomProvider(defaultProvider => new ODataSwaggerProvider(defaultProvider, c));
example:
.NET
public int Id { get; set; }
public string Name { get; set; }
public bool ReadPermission { get; set; }
public bool WritePermission { get; set; }
public ICollection IPWhitelist { get; set; }
public string ApiKey { get; set; }
public string ApiSecret { get; set; }
public bool IsActive { get; set; }
JSON
{
"Id": 0,
"Name": "string",
"ReadPermission": true,
"WritePermission": true,
"ApiKey": "string",
"ApiSecret": "string",
"IsActive": true
}
This will enable IDocumentFilter, IOperationFilter, etc. allowing swagger ouput to be post-modified using standard Swashbuckle SwaggerConfig.cs.
Hi,
In the latest version (v.2.12.0), I get this error when opening swagger UI. In version 2.1.1 this error doesn't occur:
JavaScript runtime error: 500 : {"Message":"An error has occurred.","ExceptionMessage":"Value cannot be null.\r\nParameter name: baseUrl","ExceptionType":"System.ArgumentNullException","StackTrace":" at Flurl.Url..ctor(String baseUrl) in c:\Aldenteware\Flurl\code\Flurl\Url.cs:line 28\r\n at Flurl.StringExtensions.AppendPathSegment(String url, String segment) in c:\Aldenteware\Flurl\code\Flurl\StringExtensions.cs:line 27\r\n at Swashbuckle.OData.Descriptions.ODataSwaggerUtilities.GetPathForEntitySet(String routePrefix, IEdmEntitySet entitySet)\r\n at Swashbuckle.OData.Descriptions.EntityDataModelRouteGenerator.<>c__DisplayClass2_0.b__0(IEdmEntitySet entitySet)\r\n at System.Linq.Enumerable.WhereSelectEnumerableIterator2.MoveNext()\r\n at System.Collections.Generic.List
1.InsertRange(Int32 index, IEnumerable1 collection)\r\n at System.Collections.Generic.List
1.AddRange(IEnumerable1 collection)\r\n at Swashbuckle.OData.CollectionExtentions.AddRangeIfNotNull[T](List
1 source, IEnumerable1 collection)\r\n at Swashbuckle.OData.Descriptions.EntityDataModelRouteGenerator.Generate(ODataRoute oDataRoute)\r\n at System.Linq.Enumerable.<SelectManyIterator>d__16
2.MoveNext()\r\n at System.Linq.Enumerable.d__162.MoveNext()\r\n at System.Linq.Enumerable.<SelectManyIterator>d__16
2.MoveNext()\r\n at System.Linq.Enumerable.d__162.MoveNext()\r\n at System.Linq.Enumerable.<SelectManyIterator>d__16
2.MoveNext()\r\n at System.Linq.Enumerable.d__631.MoveNext()\r\n at System.Collections.Generic.List
1..ctor(IEnumerable1 collection)\r\n at System.Linq.Enumerable.ToList[TSource](IEnumerable
1 source)\r\n at Swashbuckle.OData.CollectionExtentions.ToCollection[T](IEnumerable1 source)\r\n at Swashbuckle.OData.Descriptions.ODataApiExplorer.GetApiDescriptions()\r\n at System.Lazy
1.CreateValue()\r\n at System.Lazy1.LazyInitValue()\r\n at System.Lazy
1.get_Value()\r\n at Swashbuckle.OData.Descriptions.ODataApiExplorer.get_ApiDescriptions()\r\n at Swashbuckle.OData.ODataSwaggerProvider.GetApiDescriptionsFor(String apiVersion)\r\n at Swashbuckle.OData.ODataSwaggerProvider.GetSwagger(String rootUrl, String apiVersion)\r\n at Swashbuckle.Application.SwaggerDocsHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)\r\n at System.Net.Http.HttpMessageInvoker.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)\r\n at System.Web.Http.Dispatcher.HttpRoutingDispatcher.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)\r\n at System.Net.Http.DelegatingHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)\r\n at System.Web.Http.HttpServer.<>n__FabricatedMethod9(HttpRequestMessage , CancellationToken )\r\n at System.Web.Http.HttpServer.d__0.MoveNext()"}
I'm testing a simple Web API project with OData and hosted with OWIN.
First this is great work. I hope WebAPI OData team will work their implementation into swashbuckle like this - out of the box.
OData/WebApi#302
OData/WebApi#520
OData/WebApi#552
I wonder if you can get global ODataRoutes to eliminate the '/odata' magic string:
something like:
var odataRoutes = GlobalConfiguration.Configuration.Routes.Where(a=>a.GetType() == typeof(ODataRoute)).ToList();
if (!odataRoutes.Any()) return;
var route = odataRoutes.FirstOrDefault() as ODataRoute;
var path = "/" + route.RoutePrefix;
c.CustomProvider(defaultProvider => new ODataSwaggerProvider(path, edmModel));
Or better yet, maybe do it in ODataSwaggerProvider so you don't need to pass edm through SwaggerConfig.
PS - side question: have you tested to see whether swashbuckle filters work to post-modify swagger output? CUrious if IOperationFilter works for custom ISwaggerProvider.
Keep up the great work!
May or may not be related to #24
I noticed that when you don't implement a potential route such as a "PUT" an exception gets thrown
at System.Web.Http.Controllers.ApiControllerActionSelector.ActionSelectorCacheItem.GetInitialCandidateList(HttpControllerContext controllerContext, Boolean ignoreVerbs)
at System.Web.Http.Controllers.ApiControllerActionSelector.ActionSelectorCacheItem.GetInitialCandidateWithParameterListForRegularRoutes(HttpControllerContext controllerContext, Boolean ignoreVerbs)
at System.Web.Http.Controllers.ApiControllerActionSelector.ActionSelectorCacheItem.FindMatchingActions(HttpControllerContext controllerContext, Boolean ignoreVerbs)
at System.Web.Http.Controllers.ApiControllerActionSelector.ActionSelectorCacheItem.SelectAction(HttpControllerContext controllerContext)
at System.Web.Http.Controllers.ApiControllerActionSelector.SelectAction(HttpControllerContext controllerContext)
at System.Web.OData.Routing.ODataActionSelector.SelectAction(HttpControllerContext controllerContext)
at Swashbuckle.OData.Descriptions.HttpRequestMessageExtensions.GetHttpActionDescriptor(HttpRequestMessage request)
I think there is a chance that an application would not want to implement some of these routes, but they would have to deal with this exception being thrown. Also if you just return null from your SelectAction method for your custom route, I don't believe the Swashbuckle.Odata library will handle it gracefully and will either crash or not load the rest of the controller for the swagger UI.
Thanks,
D
Is it possible to add a custom route when using RESTier? I was not able to accomplish that... Do I have to use the CustomNavigationPropertyRoutingConvention? At the moment Operations are only created for top level entities (e.g. GET /Accounts(id) or GET /Locations(id)) but for linked entities are no operation available (e.g. I would like to see: GET /Accounts(id)/Locations)
Any hints how to accomplish that?
Hi,
In my project, I overrided the EntitySetRoutingConvention class that override the selectAction and selectController method for custom routing. The swagger doesn't seem to pick up the correct routes.
Not sure if this is really an issue vs. a question.
Example:
Actual route: /Products(1)/Suppliers Controller in this case: Suppliers
Swagger display route: /Products Controller in this case: Products
since it only recognizes the default controllers vs. the custom routing where it would return supplies as the controller)
Is there any specific Annotation that can resolve this issue?
Thanks!
I believe the original code in ODataSwaggerUitlites included a generic default response on every operation which referenced the error schema in definitions section. Somewhere along the line this got lost. It would be great to fix so that every operation gets something like:
"responses": {
"200": {...
},
"default": {
"description": "Unexpected error",
"schema": { "$ref": "#/definitions/_Error" }
}
}
And also include schema in the definition section something like:
"definitions": {
"_Error": { "properties": { "error": { "$ref": "#/definitions/_InError" } } },
"_InError": {
"properties": {
"code": { "type": "string" },
"message": { "type": "string" }
}
}
}
Hi,
Having upgraded to .Net 4.6 and added this to the project we are now trying out the documentation created. It appears this creates a standard set of operations (get, post, patch, delete). Our OData controllers never implement patch, and instead implement put. Also not all controllers implement the full range of operations. Am I missing something, or is there no way for it to discover the API methods, or to enable or disable methods such as patch and put?
It seems that with one of my controller, it requires that you have a parameter less Constructor. If one isn't present, an exception will be thrown when you try to load the swagger UI. Is this a specific requirement for using swashbuckle Odata?
500 : {
"Message": "An error has occurred.",
"ExceptionMessage": "An error occurred when trying to create a controller of type 'ProductsController'. Make sure that the controller has a parameterless public constructor.",
"ExceptionType": "System.InvalidOperationException",
"StackTrace": " at System.Web.Http.Dispatcher.DefaultHttpControllerActivator.Create(HttpRequestMessage request, HttpControllerDescriptor controllerDescriptor, Type controllerType)\r\n at System.Web.Http.Controllers.HttpControllerDescriptor.CreateController(HttpRequestMessage request)\r\n at Swashbuckle.OData.Descriptions.HttpRequestMessageExtensions.GetHttpActionDescriptor(HttpRequestMessage request)\r\n at Swashbuckle.OData.Descriptions.SwaggerRouteStrategy.GetActionDescriptors(HttpMethod httpMethod, Operation potentialOperation, String potentialPathTemplate, ODataRoute oDataRoute, HttpConfiguration httpConfig)\r\n at Swashbuckle.OData.Descriptions.SwaggerRouteStrategy.GetActionDescriptors(SwaggerRoute potentialSwaggerRoute, HttpConfiguration httpConfig)\r\n at Swashbuckle.OData.Descriptions.SwaggerRouteStrategy.<>c__DisplayClass3_0.b__1(SwaggerRoute potentialSwaggerRoute)\r\n at System.Linq.Enumerable.d__12.MoveNext()\r\n at System.Linq.Enumerable.<SelectManyIterator>d__1
2.MoveNext()\r\n at System.Linq.Enumerable.d__12.MoveNext()\r\n at System.Linq.Enumerable.<DistinctIterator>d__1
1.MoveNext()\r\n at System.Collections.Generic.List1..ctor(IEnumerable
1 collection)\r\n at System.Linq.Enumerable.ToList[TSource](IEnumerable1 source)\r\n at Swashbuckle.OData.CollectionExtentions.ToCollection[T](IEnumerable
1 source)\r\n at Swashbuckle.OData.Descriptions.ODataApiExplorer.GetApiDescriptions()\r\n at System.Lazy1.CreateValue()\r\n at System.Lazy
1.LazyInitValue()\r\n at System.Lazy1.get_Value()\r\n at Swashbuckle.OData.Descriptions.ODataApiExplorer.get_ApiDescriptions()\r\n at Swashbuckle.OData.ODataSwaggerProvider.GetApiDescriptionsFor(String apiVersion)\r\n at Swashbuckle.OData.ODataSwaggerProvider.GetSwagger(String rootUrl, String apiVersion)\r\n at Swashbuckle.Application.SwaggerDocsHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)\r\n at System.Net.Http.HttpMessageInvoker.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)\r\n at System.Web.Http.Dispatcher.HttpRoutingDispatcher.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)\r\n at System.Net.Http.DelegatingHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)\r\n at System.Web.Http.Cors.CorsMessageHandler.<>n__FabricatedMethod3(HttpRequestMessage , CancellationToken )\r\n at System.Web.Http.Cors.CorsMessageHandler.<SendAsync>d__0.MoveNext()\r\n--- End of stack trace from previous location where exception was thrown ---\r\n at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)\r\n at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n at System.Runtime.CompilerServices.TaskAwaiter
1.GetResult()\r\n at System.Web.Http.HttpServer.d__0.MoveNext()",
"InnerException": {
"Message": "An error has occurred.",
"ExceptionMessage": "Type ProductsController' does not have a default constructor",
"ExceptionType": "System.ArgumentException",
"StackTrace": " at System.Linq.Expressions.Expression.New(Type type)\r\n at System.Web.Http.Internal.TypeActivator.Create[TBase](Type instanceType)\r\n at System.Web.Http.Dispatcher.DefaultHttpControllerActivator.GetInstanceOrActivator(HttpRequestMessage request, Type controllerType, Func`1& activator)\r\n at System.Web.Http.Dispatcher.DefaultHttpControllerActivator.Create(HttpRequestMessage request, HttpControllerDescriptor controllerDescriptor, Type controllerType)"
}
} http://localhost:4448/swagger/docs/v1
Thanks,
D
We use our own logic to generate operation id's.It would be very helpfull if there would be an option to turn off the EnsureUniqueOperationIdsFilter when setting up Swashbuckle.OData in the Swagger pipeline.
Thanks in advance.
I have setup a Odata project based on the sample using the customprovider and document filter, however I receive this error when generating the UI:
0x800a139e - JavaScript runtime error: 500 : {"Message":"An error has occurred.","ExceptionMessage":"Nullable object must have a value.","ExceptionType":"System.InvalidOperationException","StackTrace":" at System.ThrowHelper.ThrowInvalidOperationException(ExceptionResource resource)\r\n at System.Nullable1.get_Value()\r\n at Swashbuckle.OData.MapToDefault.Map(Parameter parameter, Int32 index, HttpActionDescriptor actionDescriptor)\r\n at Swashbuckle.OData.SwaggerToApiExplorerMapper.<>c__DisplayClass2_0.<Map>b__0(IParameterMapper mapper)\r\n at System.Linq.Enumerable.WhereSelectListIterator
2.MoveNext()\r\n at System.Linq.Enumerable.FirstOrDefault[TSource](IEnumerable1 source, Func
2 predicate)\r\n at Swashbuckle.OData.SwaggerToApiExplorerMapper.Map(Parameter parameter, Int32 index, HttpActionDescriptor actionDescriptor)\r\n at Swashbuckle.OData.ODataApiExplorer.GetHttpParameterDescriptor(Parameter parameter, Int32 index, HttpActionDescriptor actionDescriptor)\r\n at Swashbuckle.OData.ODataApiExplorer.GetParameterDescription(Parameter parameter, Int32 index, HttpActionDescriptor actionDescriptor)\r\n at Swashbuckle.OData.ODataApiExplorer.<>c__DisplayClass19_0.b__0(Parameter parameter, Int32 index)\r\n at System.Linq.Enumerable.d__52.MoveNext()\r\n at System.Collections.Generic.List
1..ctor(IEnumerable1 collection)\r\n at System.Linq.Enumerable.ToList[TSource](IEnumerable
1 source)\r\n at Swashbuckle.OData.ODataApiExplorer.CreateParameterDescriptions(Operation operation, HttpActionDescriptor actionDescriptor)\r\n at Swashbuckle.OData.ODataApiExplorer.GetApiDescription(HttpActionDescriptor actionDescriptor, HttpMethod httpMethod, Operation operation, ODataRoute route, String potentialPathTemplate)\r\n at Swashbuckle.OData.ODataApiExplorer.GetApiDescription(HttpMethod httpMethod, ODataRoute oDataRoute, String potentialPathTemplate, Operation potentialOperation, HttpControllerDescriptor controllerDesciptor, HttpRequestMessage request, HttpRequestContext requestContext)\r\n at Swashbuckle.OData.ODataApiExplorer.GetApiDescription(HttpMethod httpMethod, ODataRoute oDataRoute, String potentialPathTemplate, Operation potentialOperation)\r\n at Swashbuckle.OData.ODataApiExplorer.GetApiDescriptions(ODataRoute oDataRoute, String potentialPathTemplate, PathItem potentialOperations)\r\n at Swashbuckle.OData.ODataApiExplorer.GetApiDescriptions(ODataRoute oDataRoute)\r\n at Swashbuckle.OData.ODataApiExplorer.GetApiDescriptions()\r\n at System.Lazy1.CreateValue()\r\n at System.Lazy
1.LazyInitValue()\r\n at System.Lazy`1.get_Value()\r\n at Swashbuckle.OData.ODataApiExplorer.get_ApiDescriptions()\r\n at Swashbuckle.OData.ODataSwaggerProvider.GetApiDescriptionsFor(String apiVersion)\r\n at Swashbuckle.OData.ODataSwaggerProvider.GetSwagger(String rootUrl, String apiVersion)\r\n at Swashbuckle.Application.SwaggerDocsHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)\r\n at System.Net.Http.HttpMessageInvoker.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)\r\n at System.Web.Http.Dispatcher.HttpRoutingDispatcher.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)\r\n at System.Net.Http.DelegatingHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)\r\n at System.Web.Http.HttpServer.<>n__FabricatedMethod9(HttpRequestMessage , CancellationToken )\r\n at System.Web.Http.HttpServer.d__0.MoveNext()"} http://localhost:2015/swagger/docs/v1
Hi
I am getting the following error when accessing /swagger for
Swashbuckle.Core" version="5.3.1"
Swashbuckle.OData" version="2.16.0"
System.MissingMethodException: Method not found: 'Void Swashbuckle.Swagger.SchemaRegistry..ctor(Newtonsoft.Json.JsonSerializerSettings, System.Collections.Generic.IDictionary2<System.Type,System.Func
1<Swashbuckle.Swagger.Schema>>, System.Collections.Generic.IEnumerable1<Swashbuckle.Swagger.ISchemaFilter>, System.Collections.Generic.IEnumerable
1<Swashbuckle.Swagger.IModelFilter>, Boolean, System.Func`2<System.Type,System.String>, Boolean, Boolean)'
at Swashbuckle.OData.ODataSwaggerProvider.GetSwagger(String rootUrl, String apiVersion)
at Swashbuckle.Application.SwaggerDocsHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
....
Thanks
Jürg
Hi,
I'm not sure if this is really an issue, but an observation that I notice. I'm not sure if this is related to an issue with swashbuckle or is it swashbuckle.OData.
I noticed that when you take the swagger json via "swagger/docs/v1" and copy and paste it on http://editor.swagger.io/#/, there are some potential issues/improvements:
Example: if the URL is "localhost/api/vi/Products GET
Generated json will display: "GET /api/vi/Products" If the baseURL is used, then you can display "GET /Products/" instead.
"consumes": [],
"produces": [],
If these two fields are not filled in, then you will not be able to use the "Try this" for the end point on the swagger UI and an error "Object is missing the required property 'accept'"
It's expecting the "produces": ["application/json"]". If all the endpoints, accept the same it can be marked at the higher level then each endpoint.
However, its interesting the the swagger UI that is generated doesn't have this issue, but the generated .json does. I would expect it to be consistent in terms of the swagger UI and the swagger json that represents the UI to be consistent.
Thanks,
D
The $expand option is not showing up on paths created to query a single entity from an EntitySet. It looks like it is being added in the ODataSwaggerUtilities.CreateSwaggerPathForEntity() method. But for some reason it seems to not be part of the PathItem after all is said and done.
I'm looking specifically at this code:
get = new Operation()
.Summary("Get entity from " + entitySet.Name + " by key.")
.OperationId(entitySet.Name + "_GetById")
.Description("Returns the entity with the key from " + entitySet.Name)
.Tags(entitySet.Name)
.Parameters(keyParameters.DeepClone()
.Parameter("$expand", "query", "Expands related entities inline.", "string", false))
.Parameters(keyParameters.DeepClone()
.Parameter("$select", "query", "Selects which properties to include in the response.", "string", false))
.Responses(new Dictionary<string, Response>().Response("200", "EntitySet " + entitySet.Name, entitySet.GetEntityType()).DefaultErrorResponse()),
I am using the sample application to test this.
Hi there @rbeauchamp, curious how to get the camelCasing to work? I think your live example also does not have. Do you not need or want that? A few weeks ago I tried using techniques found online for WEB API 2, but to no avail. thx
Unfortunately, I don't think ODataAPIExplorer and RESTier are friendly.
I get the following exception:
500 : {"Message":"An error has occurred.","ExceptionMessage":"Object reference not set to an instance of an object.","ExceptionType":"System.NullReferenceException","StackTrace":" at Microsoft.Restier.WebApi.Routing.RestierRoutingConvention.HasControllerForEntitySetOrSingleton(ODataPath odataPath, HttpRequestMessage request)\r\n at Microsoft.Restier.WebApi.Routing.RestierRoutingConvention.SelectController(ODataPath odataPath, HttpRequestMessage request)\r\n at Swashbuckle.OData.ODataApiExplorer.<>c__DisplayClass27_0.b__0(IODataRoutingConvention routingConvention)\r\n at System.Linq.Enumerable.WhereSelectEnumerableIterator 2.MoveNext()\r\n at System.Linq.Enumerable.FirstOrDefault[TSource](IEnumerable 1 source, Func 2 predicate)\r\n at Swashbuckle.OData.ODataApiExplorer.GetControllerName(ODataPathRouteConstraint oDataPathRouteConstraint, ODataPath path)\r\n at Swashbuckle.OData.ODataApiExplorer.GetControllerDesciptor(ODataRoute oDataRoute, ODataPath potentialPath)\r\n at Swashbuckle.OData.ODataApiExplorer.GetApiDescription(HttpMethod httpMethod, ODataRoute oDataRoute, String potentialPathTemplate, Operation potentialOperation)\r\n at Swashbuckle.OData.ODataApiExplorer.GetApiDescriptions(ODataRoute oDataRoute, String potentialPathTemplate, PathItem potentialOperations)\r\n at Swashbuckle.OData.ODataApiExplorer.GetApiDescriptions(ODataRoute oDataRoute)\r\n at Swashbuckle.OData.ODataApiExplorer.GetApiDescriptions()\r\n at System.Lazy 1.CreateValue()\r\n at System.Lazy 1.LazyInitValue()\r\n at System.Lazy 1.get_Value()\r\n at Swashbuckle.OData.ODataApiExplorer.get_ApiDescriptions()\r\n at Swashbuckle.OData.ODataSwaggerProvider.GetApiDescriptionsFor(String apiVersion)\r\n at Swashbuckle.OData.ODataSwaggerProvider.GetSwagger(String rootUrl, String apiVersion)\r\n at Swashbuckle.Application.SwaggerDocsHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)\r\n at System.Net.Http.HttpMessageInvoker.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)\r\n at System.Web.Http.Dispatcher.HttpRoutingDispatcher.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)\r\n at System.Net.Http.DelegatingHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)\r\n at System.Web.Http.HttpServer.d__0.MoveNext()"} http://localhost:57336/swagger/docs/v1
I'm having issue displaying other custom route other then HttpMethod.Post & HttpMethod.Get
Controller signature:
public async Task Delete([FromODataUri] int customerId, [FromODataUri] int orderID)
{
...
}
public async Task Patch([FromODataUri] int customerId,[FromODataUri] int orderID, Order order)
{
...
}
Config file:
config.AddCustomSwaggerRoute(customODataRoute, "/Customers({Id})/Orders/({orderID}")
.Operation(HttpMethod.Delete)
.PathParameter("Id")
.PathParameter("orderID");
config.AddCustomSwaggerRoute(customODataRoute, "/Customers({Id})/Orders/({orderID}")
.Operation(new HttpMethod("PATCH"))
.PathParameter("Id")
.PathParameter("orderID")
.BodyParameter("order");
Thanks,
D
Is there a way to limit graph nesting, like telling it to include no deeper than 2 levels of navigation properties?
I'm not sure if this is an Swashbuckle.OData issue or a Swashbuckle issue, but we have a model being returned that, in the way it is used, would normally have null navigation properties at the second or third level, but Swashbuckle.OData is outputting a response object graph that is like 10 levels deep, so the browser locks up for a long time or blows up with out of memory exceptions.
By default the route created with swagger for the delete operation looks like this:
https://localhost:44300/odata/v1/SafetyEquipment(testthis!)
but that wont work because the way to use a string based key is to surround the string with quotes in the parenthesis like so:
https://localhost:44300/odata/v1/SafetyEquipment('testthis!')
The result is a 404 because that route (the one without quotes) doesn't exist.
from the controller:
public virtual async Task<IHttpActionResult> Delete([FromODataUri] string key)
{
// delete tasks
}
We are running into an issue with the path templates not being generated correctly when using an ODataRoutePrefix. Our OData endpoint is set up in the following way.
Config
config.MapODataServiceRoute("AdminOData", "api/v1/admin", ODataEdmModelProvider.GetEdmModel());
Controller
[ODataRoutePrefix("products")]
public class ProductsController : ODataController
{
[EnableQuery]
[ODataRoute("({key})/attributevalues({attributevalueKey})")]
public IHttpActionResult GetAttributeValue(Guid key, Guid attributevalueKey)
{
return this.Ok();
}
}
It looks like the ODataActionDescriptor is created by the AttributeRouteStrategy. To generate the path template it looks like it is using the ODataRoute prefix (in this scenario "api/v1/admin") and ODataRouteAttribute.PathTemplate (in this scenario "({key})/attributevalues({attributevalueKey})") to generate the path template. The resulting path template ends up looking like the following.
api/v1/admin/({key})/attributevalues({attributevalueKey})
I would expect it to be
api/v1/admin/products({key})/attributevalues({attributevalueKey})
I think the same logic that is being used by AttributeRoutingConvention.GetODataPathTemplate would be needed to generate the correct path.
If a ODataRoute is registered with a route prefix that contains parameters I received the following exception when requesting the swagger route.
Ex) Within a WebApiConfig class or similar
config.MapODataServiceRoute(
routeName: "ODataRoute",
routePrefix: "{version}/{tenant}",
model: builder.GetEdmModel()
);
Exception Message
The path variable 'VERSION' in the UriTemplate must be bound to a non-empty string value. Parameter name: parameters
Stack Trace
at System.UriTemplate.VariablesCollection.LoadDefaultsAndValidate(String[] normalizedParameters, Int32& lastNonDefaultPathParameter, Int32& lastNonNullablePathParameter) at System.UriTemplate.VariablesCollection.ProcessDefaultsAndCreateBindInfo(Boolean omitDefaults, String[] normalizedParameters, IDictionary2 extraParameters, BindInformation& bindInfo) at System.UriTemplate.VariablesCollection.PrepareBindInformation(IDictionary
2 parameters, Boolean omitDefaults) at System.UriTemplate.BindByName(Uri baseAddress, IDictionary2 parameters, Boolean omitDefaults) at Swashbuckle.OData.Descriptions.OperationExtensions.GenerateSampleODataAbsoluteUri(Operation operation, String serviceRoot, String pathTemplate) at Swashbuckle.OData.Descriptions.SwaggerRouteStrategy.CreateHttpRequestMessage(HttpMethod httpMethod, Operation potentialOperation, String potentialPathTemplate, ODataRoute oDataRoute, HttpConfiguration httpConfig) at Swashbuckle.OData.Descriptions.SwaggerRouteStrategy.GetActionDescriptors(HttpMethod httpMethod, Operation potentialOperation, String potentialPathTemplate, ODataRoute oDataRoute, HttpConfiguration httpConfig) at Swashbuckle.OData.Descriptions.SwaggerRouteStrategy.GetActionDescriptors(SwaggerRoute potentialSwaggerRoute, HttpConfiguration httpConfig) at Swashbuckle.OData.Descriptions.SwaggerRouteStrategy.<>c__DisplayClass3_0.<Generate>b__1(SwaggerRoute potentialSwaggerRoute) at System.Linq.Enumerable.<SelectManyIterator>d__16
2.MoveNext() at System.Linq.Enumerable.d__162.MoveNext() at System.Linq.Enumerable.<SelectManyIterator>d__16
2.MoveNext() at System.Linq.Enumerable.d__631.MoveNext() at System.Collections.Generic.List
1..ctor(IEnumerable1 collection) at System.Linq.Enumerable.ToList[TSource](IEnumerable
1 source) at Swashbuckle.OData.CollectionExtentions.ToCollection[T](IEnumerable1 source) at Swashbuckle.OData.Descriptions.ODataApiExplorer.GetApiDescriptions() at System.Lazy
1.CreateValue() at System.Lazy`1.LazyInitValue() at Swashbuckle.OData.Descriptions.ODataApiExplorer.get_ApiDescriptions() at Swashbuckle.OData.ODataSwaggerProvider.GetApiDescriptionsFor(String apiVersion) at Swashbuckle.OData.ODataSwaggerProvider.GetSwagger(String rootUrl, String apiVersion) at Swashbuckle.Application.SwaggerDocsHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) at System.Net.Http.HttpMessageInvoker.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) at System.Web.Http.Dispatcher.HttpRoutingDispatcher.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) at System.Net.Http.DelegatingHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) at System.Web.Http.HttpServer.<>n__FabricatedMethod9(HttpRequestMessage , CancellationToken ) at System.Web.Http.HttpServer.d__0.MoveNext()
When using automatic property name conversion via the EnableLowerCamelCase() method on the ODataConventionModelBuilder, the models displayed in the Swagger UI do not match the models in the payload.
For example, the C# model will have a property called 'Id' The service is returning the property as 'id' in the payload. The Model and Model Schema sections are displaying the C# property name in Pascal case.
I have prepared an example to illustrate the issue. Please refer to Swashbuckle.OData.Sample project in the branch below:
https://github.com/bigred8982/Swashbuckle.OData/tree/property_casing_sample
The following error happens just after Install-Package Swashbuckle.OData and the adding of c.CustomProvider(defaultProvider => new ODataSwaggerProvider(defaultProvider, c)); into my existing Swagger config file.
Any idea?
500 : {"Message":"An error has occurred.","ExceptionMessage":"Value cannot be null.\r\nParameter name: virtualPath","ExceptionType":"System.ArgumentNullException","StackTrace":" at System.Web.VirtualPath.Create(String virtualPath, VirtualPathOptions options)\r\n at System.Web.Hosting.MapPathBasedVirtualPathProvider.CacheLookupOrInsert(String virtualPath, Boolean isFile)\r\n at System.Web.Routing.RouteCollection.IsRouteToExistingFile(HttpContextBase httpContext)\r\n at System.Web.Routing.RouteCollection.GetRouteData(HttpContextBase httpContext)\r\n at System.Web.Http.WebHost.Routing.HostedHttpRouteCollection.GetRouteData(HttpRequestMessage request)\r\n at Swashbuckle.OData.Descriptions.SwaggerRouteStrategy.CreateHttpRequestMessage(HttpMethod httpMethod, Operation potentialOperation, String potentialPathTemplate, ODataRoute oDataRoute, HttpConfiguration httpConfig)\r\n at Swashbuckle.OData.Descriptions.SwaggerRouteStrategy.GetActionDescriptors(HttpMethod httpMethod, Operation potentialOperation, String potentialPathTemplate, ODataRoute oDataRoute, HttpConfiguration httpConfig)\r\n at Swashbuckle.OData.Descriptions.SwaggerRouteStrategy.GetActionDescriptors(SwaggerRoute potentialSwaggerRoute, HttpConfiguration httpConfig)\r\n at Swashbuckle.OData.Descriptions.SwaggerRouteStrategy.<>c__DisplayClass3_0.b__1(SwaggerRoute potentialSwaggerRoute)\r\n at System.Linq.Enumerable.d__162.MoveNext()\r\n at System.Linq.Enumerable.<SelectManyIterator>d__16
2.MoveNext()\r\n at System.Linq.Enumerable.d__162.MoveNext()\r\n at System.Linq.Enumerable.<DistinctIterator>d__63
1.MoveNext()\r\n at System.Collections.Generic.List1..ctor(IEnumerable
1 collection)\r\n at System.Linq.Enumerable.ToList[TSource](IEnumerable1 source)\r\n at Swashbuckle.OData.CollectionExtentions.ToCollection[T](IEnumerable
1 source)\r\n at Swashbuckle.OData.Descriptions.ODataApiExplorer.GetApiDescriptions()\r\n at System.Lazy1.CreateValue()\r\n at System.Lazy
1.LazyInitValue()\r\n at Swashbuckle.OData.Descriptions.ODataApiExplorer.get_ApiDescriptions()\r\n at Swashbuckle.OData.ODataSwaggerProvider.GetApiDescriptionsFor(String apiVersion)\r\n at Swashbuckle.OData.ODataSwaggerProvider.GetSwagger(String rootUrl, String apiVersion)\r\n at Swashbuckle.Application.SwaggerDocsHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)\r\n at System.Net.Http.HttpMessageInvoker.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)\r\n at System.Web.Http.Dispatcher.HttpRoutingDispatcher.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)\r\n at System.Net.Http.DelegatingHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)\r\n at System.Web.Http.Owin.PassiveAuthenticationMessageHandler.d__0.MoveNext()\r\n--- End of stack trace from previous location where exception was thrown ---\r\n at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)\r\n at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)\r\n at System.Web.Http.HttpServer.d__0.MoveNext()"} http://localhost/Easy.Docs.API/swagger/docs/v1
The response schema isn't exactly what's coming back from OData. For example, when you request Orders_Get the response body contains a context string followed by a values aray of Order objects like this:
{
"@odata.context": "https://microsoft-apiapp18a6d.azurewebsites.net/Northwind/v4/$metadata#Orders",
"value": [
{
"OrderID": 10248,
"CustomerID": "VINET",
"EmployeeID": 5,
"OrderDate": "1996-07-04",
"RequiredDate": "1996-08-01",
"ShippedDate": "1996-07-16",
"ShipVia": 3,
"Freight": 32.38,
"ShipName": "Vins et alcools Chevalier",
"ShipAddress": "59 rue de l'Abbaye",
"ShipCity": "Reims",
"ShipRegion": null,
"ShipPostalCode": "51100",
"ShipCountry": "France"
},
{...}
]}
With the following swagger we're not describing that the data will be inside the value array property:
"responses": {
"200": {
"description": "EntitySet Orders",
"schema": { "$ref": "#/definitions/NorthwindAPI.Models.Order" }
},
"default": {
"description": "Unexpected error",
"schema": { "$ref": "#/definitions/_Error" }
}
As a result, some tools don't like it.
Perhaps this schema in swagger output would be better:
"responses": {
"200": {
"description": "OK",
"schema": {
"title": "response",
"type": "object",
"properties": {
"context": {
"type": "string"
},
"value": {
"type": "array",
"items": {
"$ref": "#/definitions/Order"
}
}
}
}
},
},
I'd like to do some more testing to see how this change might be beneficial, but reached a dead end so thought I'd share the idea if you're interested.
Hi,
I was wondering if this library supports OData Actions. If so, how would you trigger the UI to pick up the ODataActions?
Thanks,
D
After upgrading to 2.6.1, my controllers are not rendering on the UI.
The swagger ui screen just display "Finished Loading Resource Information. Rendering Swagger UI..." and never loads the controllers.
Did anything change between those versions?
I actually was able to narrow it down when it started breaking and that was from version 2.0.0 to version 2.1.0. In my project, I injected a custom route which implements the SelectAction & SelectController methods. Surprisingly in 2.0.0, when you trigger the swagger UI the code will hit the SelectionAction and the controllerContext will not be null. However, in 2.1.0+ the controllerContext is null and that is why the Swagger UI does not render any of the controllers or actions.
Any idea of what could have caused this?
So it seems the issue is because from 2.0.0 to 2.1.1, more stricter logic was put into place. For some reason Swashbuckle.OData would crap out and the exception would never be thrown or never be displayed in the UI. As a result, controllers that are process after that exception never gets rendered in the swagger UI. I believe it process the controllers in ABC order.
I was also able to isolate the problem even further. There is three problems that I've seen.
[EnableQuery]
public Task<IHttpActionResult> Get(int value)
{
throw new NotImplementedException();
}
This will throw an exception "{"Processing of the HTTP request resulted in an exception. Please see the HTTP response returned by the 'Response' property of this exception for details."}"
Inner Exception: "
at System.Web.Http.Controllers.ApiControllerActionSelector.ActionSelectorCacheItem.SelectAction(HttpControllerContext controllerContext)
at System.Web.Http.Controllers.ApiControllerActionSelector.SelectAction(HttpControllerContext controllerContext)
at System.Web.OData.Routing.ODataActionSelector.SelectAction(HttpControllerContext controllerContext)
at Swashbuckle.OData.Descriptions.HttpRequestMessageExtensions.GetHttpActionDescriptor(HttpRequestMessage request)
"
[EnableQuery]
public Task<IHttpActionResult> Get(ODataQueryOptions<Customer> queryOptions)
{
throw new NotImplementedException();
}
This will actually cause the controller to be not rendered completely on the swagger UI. This will also prevent any other processing of any further controllers that have not be processed. The UI will only display the controller that were processed before the issue occurred.
BTW: the current sample that is checked in goes in an infinite loop and never the swagger UI never loads. the 2.6.1 tagged sample. Also it doesn't compile due to some minor issue that can be fixed easily.
Let me know if this make sense,
You can actually just add to the project i linked you before and just add the methods I mentioned above to reproduce the problems.
Thanks,
Derek
In the article, it mentions Creating/Deleting a Relationship Between Entities for Odata v4 WebApi2:
Here is the link: http://www.asp.net/web-api/overview/odata-support-in-aspnet-web-api/odata-v4/entity-relations-in-odata-v4
Does the swashbuckle.Odata support $ref and also does custom setting need to be done to make it show up?
I haven't had a chance to really take a look at the inner workings of the swashbuckle.OData code base, so please bear with me.
Here are some suggestions (not sure how difficult or complex it would be):
Problem:
The extra steps that you require the user to define the custom route seems a bit much. The reason being is that the user would have to do this for every single route at this point because of the injected custom route class. From my understanding the key appeal of swagger/swashbuckle is that it automatically scans the code with little additional work on the user side to display the endpoints. If that is true, here are some suggestions:
Suggestion:
If you choose this method, is it possible to trigger off special OData attribute similar to Route(" ") on top of every function in the controller for custom routes. This will make the code more readable/maintainable. If the function has say a special attribute "[ODataCustomRoute("")] then trigger off that. By doing this, you wouldn't have to specify the Controller or even the parameters in the config file since you have all that info from being in the Controller class and the attribute would be connected to the function signature where you can attract the params.
If that is too complicated, you can just have the user associate the attributes at the controller level.
Another possible way to resolve the issue, is it possible to take advantage of the return functions of the custom route class.
I'm not really sure how the code scan for routes works, so not sure if this is possible. However, The custom route class overrides two functions. SelectAction & SelectController. When the application starts up & swagger OData starts scanning the code for endpoints, is it possible to use the custom route class to retrieve the Controller (This eliminates the need to specify the controller) and also the action (eliminates the need to specify the action). By using reflection, is it possible to scan for function in the controller and the parameters. These would indicate the possible routes no? There is probably a lot of details that i'm leaving out which makes it difficult.
Please let me know if the suggestions make sense,
Thanks,
Derek
I get an exception on SendAsync of my main handler when the request for http://localhost/swagger/ui/index comes in.
The exception is "An item with the same key has already been added" and the stacktrace is added below. It only happens when adding c.CustomProvider((defaultProvider) => new ODataSwaggerProvider(defaultProvider, c));
to the SwaggerConfig
, otherwise Swashbuckle works fine, only with the non-OData controllers in my project.
---Stacktrace---
at System.ThrowHelper.ThrowArgumentException(ExceptionResource resource) at System.Collections.Generic.Dictionary``2.Insert(TKey key, TValue value, Boolean add) at System.Linq.Enumerable.ToDictionary[TSource,TKey,TElement](IEnumerable``1 source, Func``2 keySelector, Func``2 elementSelector, IEqualityComparer``1 comparer) at System.Linq.Enumerable.ToDictionary[TSource,TKey,TElement](IEnumerable``1 source, Func``2 keySelector, Func``2 elementSelector) at Swashbuckle.OData.Descriptions.OperationExtensions.GenerateSamplePathParameterValues(Operation operation) at Swashbuckle.OData.Descriptions.OperationExtensions.GenerateSampleODataUri(Operation operation, String serviceRoot, String pathTemplate) at Swashbuckle.OData.Descriptions.SwaggerRouteStrategy.CreateHttpRequestMessage(HttpMethod httpMethod, Operation potentialOperation, SwaggerRoute potentialSwaggerRoute, HttpConfiguration httpConfig) at Swashbuckle.OData.Descriptions.SwaggerRouteStrategy.GetActionDescriptors(HttpMethod httpMethod, Operation potentialOperation, SwaggerRoute potentialSwaggerRoute, HttpConfiguration httpConfig) at Swashbuckle.OData.Descriptions.SwaggerRouteStrategy.GetActionDescriptors(SwaggerRoute potentialSwaggerRoute, HttpConfiguration httpConfig) at Swashbuckle.OData.Descriptions.SwaggerRouteStrategy.<>c__DisplayClass3_0.<Generate>b__1(SwaggerRoute potentialSwaggerRoute) at System.Linq.Enumerable.<SelectManyIterator>d__16``2.MoveNext() at System.Linq.Enumerable.<SelectManyIterator>d__16``2.MoveNext() at System.Linq.Enumerable.<DistinctIterator>d__63``1.MoveNext() at System.Linq.Enumerable.<SelectManyIterator>d__16``2.MoveNext() at System.Collections.Generic.List``1..ctor(IEnumerable``1 collection) at System.Linq.Enumerable.ToList[TSource](IEnumerable``1 source) at Swashbuckle.OData.CollectionExtentions.ToCollection[T](IEnumerable``1 source) at Swashbuckle.OData.Descriptions.ODataApiExplorer.GetApiDescriptions() at System.Lazy``1.CreateValue() at System.Lazy``1.LazyInitValue() at System.Lazy``1.get_Value() at Swashbuckle.OData.Descriptions.ODataApiExplorer.get_ApiDescriptions() at Swashbuckle.OData.ODataSwaggerProvider.GetApiDescriptionsFor(String apiVersion) at Swashbuckle.OData.ODataSwaggerProvider.GetSwagger(String rootUrl, String apiVersion) at Swashbuckle.Application.SwaggerDocsHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) at System.Net.Http.HttpMessageInvoker.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) at System.Web.Http.Dispatcher.HttpRoutingDispatcher.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) at System.Net.Http.DelegatingHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) at System.Web.Http.Cors.CorsMessageHandler.<>n__FabricatedMethod3(HttpRequestMessage , CancellationToken ) at System.Web.Http.Cors.CorsMessageHandler.<SendAsync>d__0.MoveNext()
---End of stacktrace---
I'm using NuGet packages:
Swagger spec shows operations have both a summary and a description property. The summary is supposed to be short and description more verbose. Although the summary technically can be up to 120 characters, I'd suggest, for the standard operations, it should be just two words. This will help with tooling that uses the summary as the 'friendly name' for an operation.
For example:
"/Northwind/v4/Orders": {
"get": {
"tags": [ "Orders" ],
"summary": "Get Orders",
"description": "Returns the EntitySet Orders",
"operationId": "Orders_Get",
...},
"post": {
"tags": [ "Orders" ],
"summary": "Create Order",
"description": "Post a new entity to EntitySet Orders",
"operationId": "Orders_Post",
...}
},
"/Northwind/v4/Orders({OrderID})": {
"get": {
"tags": [ "Orders" ],
"summary": "Get Order",
"description": "Returns the entity with the key from Orders",
"operationId": "Orders_GetByOrderid",
...},
"put": {
"tags": [ "Orders" ],
"summary": "Replace Order",
"description": "Replace entity in EntitySet Orders",
"operationId": "Orders_PutByOrderid",
...},
"delete": {
"tags": [ "Orders" ],
"summary": "Delete Order",
"description": "Delete entity in EntitySet Orders",
"operationId": "Orders_DeleteByOrderid",
...},
"patch": {
"tags": [ "Orders" ],
"summary": "Update Order",
"description": "Update entity in EntitySet Orders",
"operationId": "Orders_PatchByOrderid",
...}
},
When the swagger definitions of my API are being generated I'm missing sub objects within the graph. I've tracked it down to the LimitSchemaGraphToTopLevelEntity
IDocumentFilter
being added by default.
Can you expose a configuration property to enable/disable this?
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.