How to show <remarks> in Swagger documentation

Viewed 272

I am adding documentation to the model of our API (.Net Framework 4.7.2).

I usually use something like:

''' <summary>
''' My summary
''' </summary>
''' <remarks>My remarks...</remarks>
Public Property MyProperty() As SomeClass

When I access the Model of the Swagger documentation, I see:

MyProperty (SomeClass): My summary ,

What should I do to see also "My Remarks" (maybe when I hover on the text if not immediately after the Summary)?

Thanks

My documentation appears like this

1 Answers

Currently the <remarks> are only displayed for the controller actions, but not for the parameters.

See [Feature Request] Add remarks to parameters · Issue #1937 · domaindrivendev/Swashbuckle.AspNetCore

... the Swagger/OpenAPI Parameter object only supports a single description field for entering descriptive text. For simplicity, I'd like to maintain a 1:1 mapping between XML Comments tag and Swagger/OpenAPI field as opposed to combining/formatting multiple tags into the one field. So, your request is not something I plan on adding to SB at this point.

But I found out it could be added with custom IParameterFilter

/// <summary>
/// Add the content of &lt;remarks&gt; to the description of a parameter
/// Inspired by <see cref="Swashbuckle.AspNetCore.SwaggerGen.XmlCommentsParameterFilter"/>
/// </summary>
public class AddRemarksToParameterDescription : IParameterFilter
{
    private readonly XPathNavigator _xmlNavigator;

    public AddRemarksToParameterDescription(XPathDocument xmlDoc)
    {
        _xmlNavigator = xmlDoc.CreateNavigator()!;
    }

    public void Apply(OpenApiParameter parameter, ParameterFilterContext context)
    {
        if (context.PropertyInfo != null)
        {
            ApplyPropertyTags(parameter, context);
        }
        else if (context.ParameterInfo != null)
        {
            ApplyParamTags(parameter, context);
        }
    }

    private void ApplyPropertyTags(OpenApiParameter parameter, ParameterFilterContext context)
    {
        var propertyMemberName = XmlCommentsNodeNameHelper.GetMemberNameForFieldOrProperty(context.PropertyInfo);
        var propertyNode = _xmlNavigator.SelectSingleNode($"/doc/members/member[@name='{propertyMemberName}']");

        if (propertyNode == null) return;

        var remarksNode = propertyNode.SelectSingleNode("remarks");
        if (remarksNode != null)
        {
            parameter.Description += FormatRemarks(remarksNode.InnerXml);
        }
    }

    private void ApplyParamTags(OpenApiParameter parameter, ParameterFilterContext context)
    {
        if (!(context.ParameterInfo.Member is MethodInfo methodInfo)) return;

        // If method is from a constructed generic type, look for comments from the generic type method
        var targetMethod = methodInfo.DeclaringType is { IsConstructedGenericType: true }
            ? methodInfo.GetUnderlyingGenericTypeMethod()
            : methodInfo;

        if (targetMethod == null) return;

        var methodMemberName = XmlCommentsNodeNameHelper.GetMemberNameForMethod(targetMethod);
        var paramNode = _xmlNavigator.SelectSingleNode(
            $"/doc/members/member[@name='{methodMemberName}']/param[@name='{context.ParameterInfo.Name}']");

        if (paramNode != null)
        {
            var remarksNode = paramNode.SelectSingleNode("remarks");
            if (remarksNode != null)
            {
                parameter.Description += FormatRemarks(remarksNode.InnerXml);
            }
        }
    }

    private static string FormatRemarks(string text)
    {
        return "<br><br>Remarks: <br><i>" + XmlCommentsTextHelper.Humanize(text) + "</i>";
    }
}

Add in your setup:

instead of this: (from the official readme)

services.AddSwaggerGen(c =>
{
    c.SwaggerDoc("v1",
        new OpenApiInfo
        {
            Title = "My API - V1",
            Version = "v1"
        }
     );

     var filePath = Path.Combine(System.AppContext.BaseDirectory, "MyApi.xml");
     c.IncludeXmlComments(filePath);
});

Do this:

services.AddSwaggerGen(c =>
{
    c.SwaggerDoc("v1",
        new OpenApiInfo
        {
            Title = "My API - V1",
            Version = "v1"
        }
    );

    var filePath = Path.Combine(System.AppContext.BaseDirectory, "MyApi.xml");

    // ==== NEW
    var xmlDox = new XPathDocument(filePath); // Re-use XPathDocument
    c.IncludeXmlComments(() => xmlDox); // IncludeXmlComments with current XPathDocument
    c.ParameterFilter<AddRemarksToParameterDescription>(xmlDox); // The new filter
});

Tested with Swashbuckle.AspNetCore 5.5 + C# 8 with nullable analysis. I think it should also work with Swashbuckle.AspNetCore 6

Related