Open brittonbeckham opened 8 years ago
I have the same issue!
Same issue here. When is 6.0.0 planned for?
Same issue - only occuring when a generic type is used as a parameter. Outputs being generic is just fine.
Same here. Can't believe this is still open. Any workaround?
Can anyone post a link to a sample project that reproduces this issue?
As far as a workaround, one way that might help is to implement an IOperationFilter and fill it in
public class MyOperationFilter : IOperationFilter
{
public void Apply( Operation operation, SchemaRegistry schemaRegistry, ApiDescription apiDescription )
{
if ( string.IsNullOrEmpty( operation.summary ) )
{
// Manually set the documentation summary if swashbuckle couldn't figure it out. Swashbuckle 5 isn't able to figure these out since they have Generic parameters
if ( apiDescription.HttpMethod.Method == "POST" )
{
operation.summary = "POST endpoint. Use this to add a record";
}
else if ( apiDescription.HttpMethod.Method == "PUT" )
{
operation.summary = "PUT endpoint. Use this to update a record";
}
}
}
}
@MikeDPeterson a workaround that I would not recommend ... but yes it is a valid workaround! I would love to troubleshoot this. do you have a simple project that can reproduce this bug?
@heldersepu I can't post a link to the project unfortunately because it is under VPN. But I can describe my situation.
There is a web API project, with an abstract base controller which has CRUD operations.
public class TestController : BaseController<TestType>
Methods of BaseController is like this:
public virtual TEntity Post(TEntity entity)
public virtual TEntity Get(string id)
public virtual TEntity Put(TEntity entity)
public virtual Task Delete(string id)
XML comments for GET and DELETE is visible. But it is not visible for PUT and POST as they have a generic type as a method parameter.
@zibrohimov Can you add the project to GitHub?
@heldersepu I'm afraid I can't because I'm not allowed to do so. But I can give you relevant information about my projects nature if need any details. Here is a screenshot about how does that look. https://www.screencast.com/t/koF4v4HNt
@zibrohimov Just help me out troubleshooting, create a simple project that reproduces the issue and add it to your GitHub
@heldersepu https://github.com/zibrohimov/SwashbuckleDemo
The error is here: https://github.com/domaindrivendev/Swashbuckle/blob/master/Swashbuckle.Core/Swagger/XmlComments/XmlCommentsIdHelper.cs#L63
that if (type.IsGenericType)
always return false
If anyone wants to take on the challenge of figure out why IsGenericType does not work as expected here is a small console application reproducing the issue:
using System;
abstract class Parent<T> {
public virtual T Put(T id, char value) {
return id;}}
class Child : Parent<int> {
public virtual string Get(Guid guid) {
return "aei";}}
class Program {
static void Main(string[] args) {
foreach (var methodInfo in typeof(Child).GetMethods()) {
if (methodInfo.IsVirtual && methodInfo.IsSecurityCritical) {
Console.WriteLine(methodInfo.Name);
foreach (var param in methodInfo.GetParameters()) {
Console.Write(" " + param.Name + " IsGenericType=");
Console.WriteLine(param.ParameterType.IsGenericType);}}}
Console.ReadKey();}}
I ran into the same issue and found the cause.
Your controller has a generic type argument TKey
that is used as argument in your Get method. In the xml documentation this argument is not expanded. You'll see something like
member name="M:Your.Namespace.ReadController`5.Get(`1, [... more ...])"
In XmlCommentsIdHelper.AppendMethodName
I've added a workaround for this (not thoroughly tested, but it works for me)
private static void AppendMethodName(MethodInfo methodInfo, StringBuilder builder)
{
builder.Append(methodInfo.Name);
var parameters = methodInfo.GetParameters();
if (parameters.Length == 0) return;
builder.Append("(");
foreach (var param in parameters)
{
//Change: if the parameter type is a generic type argument replace it with the placeholder instead of the actual type
if (methodInfo.DeclaringType.GenericTypeArguments.Contains(param.ParameterType))
{
builder.Append($"`{Array.IndexOf(methodInfo.DeclaringType.GenericTypeArguments, param.ParameterType)}");
}
else //do it the regular way
{
AppendFullTypeName(param.ParameterType, builder, true);
}
builder.Append(",");
}
builder.Replace(",", ")", builder.Length - 1, 1);
}
Ultimately, I don't think this is related to the use of a generic parameter. SB supports generic parameters and return types and has passing tests to prove it:
I think it's actually due to the use of inherited/abstract actions. By design, SB will not pull XML comments from inherited actions. Doing so would add a ton of complexity and have a significant impact on performance. Furthermore, I don't think it makes a ton of sense. Abstract/generic actions can, by their nature, only be accompanied by fairly abstract descriptions and I don't think these are particularly suitable for describing concrete API actions. "Gets the entity by the given key" isn't a particularly useful description.
With that said, I would recommend using a simple OperationFilter instead of XML comments to describe specific implementations of generic actions. Check out the following for an example. It's from the ASP.NET Core-specific SB project, has some minor syntactic differences, but should give you the idea:
Hi,
I'm hitting a somewhat related issue. I've been able to narrow down the repro to:
code snippets:
/// <summary>Gets a blob</summary>
/// <param name="id">Blob Id</param>
/// <param name="includes">Comma separated list of what to include, for example "ContentInfo,Description". Defaults to None</param>
/// <returns>The blob's metadata</returns>
[Route("blobsfail/{id}")]
[HttpGet]
[ResponseType(typeof(BlobMetadataRetrieve))]
public IHttpActionResult RetrieveBlobMetadataByIdFail(Guid id, BlobMetadataIncludes? includes = null)
{
return RunGet(() => blobRepo.RetrieveById(id, includes.GetValueOrDefault(BlobMetadataIncludes.None)));
}
/// <summary>Gets a blob</summary>
/// <param name="id">Blob Id</param>
/// <param name="includes">Comma separated list of what to include, for example "ContentInfo,Description". Defaults to None</param>
/// <returns>The blob's metadata</returns>
[Route("blobsok/{id}")]
[HttpGet]
[ResponseType(typeof(BlobMetadataRetrieve))]
public IHttpActionResult RetrieveBlobMetadataByIdOk(Guid id, BlobMetadataIncludes includes = BlobMetadataIncludes.None)
{
return RunGet(() => blobRepo.RetrieveById(id, includes));
}
This is the resulting xmldoc:
<member name="M:AssemblyName.Controllers.BlobsController`1.RetrieveBlobMetadataByIdFail(System.Guid,System.Nullable{AssemblyName.DataModel.BlobMetadataIncludes})">
<summary>Gets a blob</summary>
<param name="id">Blob Id</param>
<param name="includes">Comma separated list of what to include, for example "ContentInfo,Description". Defaults to None</param>
<returns>The blob's metadata</returns>
</member>
<member name="M:AssemblyName.Controllers.BlobsController`1.RetrieveBlobMetadataByIdOk(System.Guid,AssemblyName.DataModel.BlobMetadataIncludes)">
<summary>Gets a blob</summary>
<param name="id">Blob Id</param>
<param name="includes">Comma separated list of what to include, for example "ContentInfo,Description". Defaults to None</param>
<returns>The blob's metadata</returns>
</member>
The blobsfail route does not get its documentation populated (route summary and doc for parameters), while the blobsok one gets it. Screenshot:
Upgrading to the latest version on NuGet.org (5.6.0) did not help.
Any ideas or workarounds? Routing does not like value types with defaults, which is why we have to use nullable. Nullable works fine as long as they are not parameters of functions in templated classes.
Thanks,
Greg
@vdbg I tried to reproduce with your comments... since you didn't share the code for the "templated abstract class" I replace it with an int ...and it works fine for me: http://swashbuckletest.azurewebsites.net/swagger/ui/index?filter=Blob Code behind that is here: https://github.com/heldersepu/SwashbuckleTest/commit/7817595a15c3e88999cc0e525ee69a9ac2b2ea10
@heldersepu : updated the repro to add the "member function being part of a templated abstract class" part (both this and nullable param are required)
https://github.com/vdbg/SwashbuckleTest/commit/7d4f48f41f01904115022699f72c3ab7b1dafdf9
@vdbg Thanks that gets me a lot closer, I'm trying to get a minimal Controller reproducing this issue... So far it looks like that inheritance is not the problem https://github.com/heldersepu/SwashbuckleTest/commit/a356655fee6c3575e50f367b4a674754d54334d6
@vdbg Here is my minimal case to reproduce your scenario:
using System.Web.Http;
namespace Swagger_Test.Controllers
{
public abstract class Blob<T> : ApiController
{
/// <summary> Get a Bad Blob </summary>
public string GetBad(int? x) { return "Bad"; }
/// <summary> Get an Ok Blob </summary>
public string PostOk(int x) { return "Ok"; }
}
public class Foo { }
public class BlobController : Blob<Foo> { }
}
The problem is the combination of generics and the nullable param, If I remove any of those it work for me... Now the real troubleshooting can start!
For any one interested on scoring an easy PR the bug is here: https://github.com/domaindrivendev/Swashbuckle/blob/master/Swashbuckle.Core/Swagger/XmlComments/XmlCommentsIdHelper.cs#L75
That Replace
must be done only to the args not the whole builder
Thanks for looking into it ! What are the odds / timelines of having a fix pushed on NuGet.org ?
@vdbg I made the fix on my repo, now it loads correctly: http://swashbuckletest.azurewebsites.net/swagger/ui/index?filter=Blob
My package has a different name in nuget: https://www.nuget.org/packages/Swagger-Net/
@heldersepu : what's the difference between Swagger-Net and Swashbuckle ?
Swagger-Net was a fork of Swashbuckle. ...but I decided to detach it and rename it, that way I can fix bugs, add features and do releases at a much faster pace.
If you have time try both post back the differences you see?
Hello, so I tried the latest nuget package and I'm still hitting the same issue when it comes to enum type parameters (both nullable and non-nullable).
I created a minimal repro of the same here...
@yojagad you are bringing something new to the equation. It is not related to generics or the nullable param, I thing is because the nested enum...
@yojagad the minimal repro is much appreciated! I should have a solution shortly...
@yojagad your issue has been fixed.
@heldersepu Thank you so much! When approximately will I see the updated version on nuget.org..?
@yojagad Done the update version is on nuget.org: https://www.nuget.org/packages/Swagger-Net/8.3.3.103
Thanks @heldersepu . The fix works for the issue I mentioned.. I did uncover one another issue with respect to generic type parameters though. Added minimal repro for the same.
@yojagad I made the correction on the MyGet package, when you have time please verify and I will update Nuget
@heldersepu I tested it out from MyGet. It works. Thank you!
@yojagad I just uploaded to Nuget give it a few minutes for the indexing to complete... If you find any other issue please report it directly on Swagger-Net
@heldersepu Will do, thanks again!
XmlComments functionality need some serious refactoring. First it should be user-replaceable so that quick bugs could be fixed by users without need to go through slow and unpredictable swashbckle update process.
@PaulDotNet remember this project is OpenSource! everything is user-replaceable.
Well, if fork goes too far from the original then updating becomes problematic. I am using my own fork currently and merging changes from GitHub is not fun. Significant change may break my fork as well. Forking is a short term solution. This is the reason why I stay away from forked nugget packages. Their updates stop after a while.
Sorry @PaulDotNet but I do not see you have a fork of this project... You do have one of Swashbuckle.AspNetCore that is a different project
Hello, I'm having a similar problem where if a controller method has a generic model as [FromBody] parameter, if type of that model is set to nullable, the swagger documentation is not produced:
public ValidationMessage ValidateDateOfBirth([FromBody] SingleInput<DateTime> input)
works fine, but
public ValidationMessage ValidateDateOfBirth([FromBody] SingleInput<DateTime?> input)
doesn't work ;<
I'm on version 8.3.7.2
The xml:
<member name="M:Api.Controllers.ValidationController.ValidateDateOfBirth(Api.Models.SingleInput{System.DateTime})">
<member name="M:Api.Controllers.ValidationController.ValidateDateOfBirth(Api.Models.SingleInput{System.Nullable{System.DateTime}})">
@PaulDotNet - XML functionality is user replaceable. It’s implemented as an IOperationFilter and ISchemaFilter which are registered when you call “IncludeXmlComments”. If you want to override, don’t call this and register derived versions or wholesale replacements manually
Following - I am also having the same issue...
Just ran into this.
Same here.
Whenever I add a generic route in a generic abstact controller that is concretly derived for a specific type and hit F5 I get a error:
I am not using any abstract classes, I am just trying to make it possible to use different subtypes when posting to a controller action. I have set up a minimal repo here.
When starting with dotnet run
and navigating to http://localhost:5085/swagger/index.html one can see that there is only one endpoint with only the base class in the docs but the other with the generic type param is missing.
How can I make it work with a generic request body parameter?
In our organization, we have generic abstract controllers that we use as base controllers for all our data access APIs. The current algorithm that swagger uses to generate the documentation doesn't know how to handle this scenario because the method documentation signature has generic heuristics.
Here is an example of what the controller looks like with one of the generic methods included.
The XML that is generated for this method output by MSbuild is as follows:
Even though we are including this
XML
file in the bin folder and ensure it gets added through the swagger callIncludeXmlComments()
, no documentation is added to the route in the SwaggerUI.The issue lies in the
ApplyXmlActionComments.Apply()
method inSwashbuckle.Swagger
; essentially, it just fails to comprehend this method signature in the XML file and therefore cannot match the generic method to its documentation.Would be nice to have this scenario accounted for so as to make generic routes display documentation as desired.