I'm attempting to use swashbuckle 5.0 Alpha in a project that is also using Microsoft ASP.NET Web API 2.2 for Odata v4.0. For the most part, I am creating controllers that are inheriting from ODataController. I do have a few other controllers that are inheriting from ApiController. The ones inheriting from ApiController show up when navigating to /swagger, the ones using the ODataController do not(All the .cs controller files are in the same controllers folder).
Based on this item:https://github.com/domaindrivendev/Swashbuckle/issues/101 I was thinking they would be working, but they don't appear to be.
I am also having this issue. I have a project with both ODataControllers and ApiControllers. Swashbuckle is only recognizing the ApiControllers. Will the ability for Swashbuckle to recognize ODataControllers (for OData 4.0) be available soon or is this something that will not be possible?
Swashbuckle sits on top of WebApi's built-in metadata layer - ApiExplorer. It takes the operation descriptions from ApiExplorer and then maps them into corresponding Swagger descriptions.
You don't see descriptions for ODataController's because ApiExplorer does not provide them either. See the following for more information:
https://aspnetwebstack.codeplex.com/workitem/2107
https://aspnetwebstack.codeplex.com/workitem/1892
You could try explicitly decorating your controllers with the following ...
[ApiExplorerSettings(IgnoreApi=false)]
to override the default behavior. But as mentioned in the issues above, this can lead to incorrect descriptions. You could also wire up an IDocumentFilter and manually add the descriptions for your ODataControllers.
Thanks for the info. I tried using the [ApiExplorerSettings(IgnoreApi=false)] and it sort of works. It has some of my methods on some controllers and skips some on others. I.E.. has post and put, but no get on one of them.
Is there documentation out there about using an IDocumentFIlter?
https://github.com/domaindrivendev/Swashbuckle/tree/swagger_2.0#modifying-generated-operations
hi @gkingston @domaindrivendev , were you able to use Swashbuckle Document Filter for documenting OData controller? Any thoughts will be appreciated.
So I got something working with an IDocumentFilter and I'll share it here. I'll plop down the code and then make some remarks about it under the listing. Comments welcome. I'd be happy to try and integrate this into the Swashbuckle repo (or allow others to extend this to do so) -- but haven't actually modified GitHub code yet. Anyway, here's the code:
public class CustomDocumentFilter : IDocumentFilter
{
public void Apply(SwaggerDocument swaggerDoc, SchemaRegistry schemaRegistry, IApiExplorer apiExplorer)
{
var thisAssemblyTypes = Assembly.GetExecutingAssembly().GetTypes().ToList();
var odatacontrollers = thisAssemblyTypes.Where(t => t.IsSubclassOf(typeof(ODataController))).ToList();
var odataRoutes = GlobalConfiguration.Configuration.Routes.Where(a=>a.GetType() == typeof(ODataRoute)).ToList();
if (!odataRoutes.Any() || !odatacontrollers.Any()) return;
var odatamethods = new[] {"Get", "Put", "Post", "Delete"};
var route = odataRoutes.FirstOrDefault() as ODataRoute;
foreach (var odataContoller in odatacontrollers) // this is all of the OData controllers in your API
{
var methods = odataContoller.GetMethods().Where(a => odatamethods.Contains(a.Name)).ToList();
if (!methods.Any())
continue; // next controller -- this one doesn't have any applicable methods
foreach (var method in methods) // this is all of the methods for a SINGLE controller (e.g. GET, POST, PUT, etc)
{
var path = "/" + route.RoutePrefix + "/" + odataContoller.Name.Replace("Controller", "");
var odataPathItem = new PathItem();
var op = new Operation();
// This is assuming that all of the odata methods will be listed under a heading called OData in the swagger doc
op.tags = new List<string> { "OData" };
op.operationId = "OData_" + odataContoller.Name.Replace("Controller", "");
// This should probably be retrieved from XML code comments....
op.summary = "Summary for your method / data";
op.description = "Here is where we go deep into the description and options for the call.";
op.consumes = new List<string>();
op.produces = new List<string> { "application/atom+xml", "application/json", "text/json", "application/xml", "text/xml" };
op.deprecated = false;
var response = new Response() { description = "OK" };
response.schema = new Schema { type = "array", items = schemaRegistry.GetOrRegister(method.ReturnType) };
op.responses = new Dictionary<string, Response> {{"200", response}};
var security = GetSecurityForOperation(odataContoller);
if (security != null)
op.security = new List<IDictionary<string, IEnumerable<string>>> { security };
odataPathItem.get = op; // this needs to be a switch based on the method name
swaggerDoc.paths.Add(path, odataPathItem);
}
}
}
private Dictionary<string, IEnumerable<string>> GetSecurityForOperation(MemberInfo odataContoller)
{
Dictionary<string, IEnumerable<string>> securityEntries = null;
if (odataContoller.GetCustomAttribute(typeof (AuthorizeAttribute)) != null)
{
securityEntries = new Dictionary<string, IEnumerable<string>> {{"oauth2", new[] {"actioncenter"}}};
}
return securityEntries;
}
}
And as promised, here are some notes. The class itself is called "CustomDocumentFilter" -- you can choose any name you want. Whatever you name the class, you have to modify the SwaggerConfig.cs to use your document filter:
c.DocumentFilter<CustomDocumentFilter>();
This method is called when the rest of the Swashbuckle stuff is already done, so you have a complete SwaggerDocument and SchemaRegistry when this method is called. The ApiExplorer was somewhat useless since it didn't have the ODataControllers in it.
But the main idea in the code above is to add one or more "path" objects to the swaggerDoc, and one or more "operations" to each path. So I basically just used some reflection to get all of the ODataControllers in the assembly, along with their methods, and used the GlobalConfiguration object to get the routes that would get us into the OData controllers.
I'm not doing anything with XML comments yet, and the "odataPathItem.get = op" should be a conditional based on the method name (i.e. the "Post" method should be odataPathItem.post = op;)
HTH
Borrowing code from Microsoft.AspNet.OData, I have added support for OData v4.0. I created a custom ISwaggerProvider that converts an IEdmModel to a SwaggerDocument. Try out Swashbuckle.OData and let me know what you think.
See it in action here: http://swashbuckleodata.azurewebsites.net/swagger
I'll try to have a look, cool, thx! seemed to work. Will this be integrated? or kept as separate add-on?
There is a slight issue, i have multiple odata controllers and all of them have get,post,patch, etc operations.
And all of them has same path to be addresed to and are only differentiated by request type (POST, GET,etc) and the type of parameters.But your code @dahlsailrunner only allows it to take unique path and thus i cannot add more operations. Any help how to get this done ?
Hi @rbeauchamp auchamp
500 : {"Message":"An error has occurred.","ExceptionMessage":"Precondition failed: currentProperty != null","ExceptionType":"System.Diagnostics.Contracts.__ContractsRuntime+ContractException","StackTrace":" at System.Diagnostics.Contracts.__ContractsRuntime.TriggerFailure(ContractFailureKind kind, String msg, String userMessage, String conditionTxt, Exception inner) in C:\Users\rbeauchamp\Documents\GitHub\Swashbuckle.OData\Swashbuckle.OData\Descriptions\MapToODataActionParameter.cs:line 0\r\n at System.Diagnostics.Contracts.__ContractsRuntime.ReportFailure(ContractFailureKind kind, String msg, String conditionTxt, Exception inner) in C:\Users\rbeauchamp\Documents\GitHub\Swashbuckle.OData\Swashbuckle.OData\Descriptions\MapToODataActionParameter.cs:line 0\r\n at System.Diagnostics.Contracts.__ContractsRuntime.Requires(Boolean condition, String msg, String conditionTxt) in C:\Users\rbeauchamp\Documents\GitHub\Swashbuckle.OData\Swashbuckle.OData\Descriptions\MapToODataActionParameter.cs:line 0\r\n at Swashbuckle.OData.SchemaRegistryExtensions.GetEdmPropertyName(MemberInfo currentProperty, IEdmStructuredType edmType) in
So, I cannot use your lib for odata :(
Hi @rbeauchamp auchamp
Is it possible to have ISwaggerProvider support multiple versions?
Most helpful comment
So I got something working with an IDocumentFilter and I'll share it here. I'll plop down the code and then make some remarks about it under the listing. Comments welcome. I'd be happy to try and integrate this into the Swashbuckle repo (or allow others to extend this to do so) -- but haven't actually modified GitHub code yet. Anyway, here's the code:
And as promised, here are some notes. The class itself is called "CustomDocumentFilter" -- you can choose any name you want. Whatever you name the class, you have to modify the SwaggerConfig.cs to use your document filter:
This method is called when the rest of the Swashbuckle stuff is already done, so you have a complete SwaggerDocument and SchemaRegistry when this method is called. The ApiExplorer was somewhat useless since it didn't have the ODataControllers in it.
But the main idea in the code above is to add one or more "path" objects to the swaggerDoc, and one or more "operations" to each path. So I basically just used some reflection to get all of the ODataControllers in the assembly, along with their methods, and used the GlobalConfiguration object to get the routes that would get us into the OData controllers.
I'm not doing anything with XML comments yet, and the "odataPathItem.get = op" should be a conditional based on the method name (i.e. the "Post" method should be odataPathItem.post = op;)
HTH