在SwaggerUI中包括XML注释的Asp.Net Core API。

huangapple go评论54阅读模式
英文:

Include xml remarks in SwaggerUI Asp.Net Core API

问题

备注将呈现为操作结果。但是,只有类定义和参数的摘要会呈现。是否可以在用户界面中包含XML备注?给定以下类:

	public class Customer
	{ 
		/// <summary>
		/// 这是ID
		/// </summary>
		public int CustomerId { get; set; }

		/// <summary>
		/// 这是名称
		/// </summary>
		/// <remarks>备注未在任何地方呈现</remarks>
		public string Name { get; set; }
	}

在呈现模式时,备注未呈现:

在SwaggerUI中包括XML注释的Asp.Net Core API。

对于以下类的参数部分也适用:

	public class QueryOptions
	{
		/// <summary>
		/// 要查询的名称。
		/// </summary>
		/// <remarks>
		/// 备注未呈现
		/// </remarks>
		public string? Name { get; set; }

		/// <summary>
		/// 要查询的ID
		/// </summary>
		/// <remarks>
		/// 备注未呈现
		/// </remarks>
		public int? Id { get; set; }

	}

是否可以使用文档或模式过滤器来获取备注并将其附加到描述中?我正在使用Swashbuckle.AspNetCore的6.5.0版本。

英文:

Remarks are rendered for action results. However, only summaries are rendered for class definitions and parameters. Is it possible to include the XML remarks in the UI? Given the following class:

	public class Customer
	{ 
		/// <summary>
		/// This is the ID
		/// </summary>
		public int CustomerId { get; set; }

		/// <summary>
		/// This is the name
		/// </summary>
		/// <remarks>Remarks are not rendered anywhere</remarks>
		public string Name { get; set; }
	}

When rendering schemas, the remarks are not rendered
在SwaggerUI中包括XML注释的Asp.Net Core API。

The same applies for the Parameters section given the following class:

	public class QueryOptions
	{
		/// <summary>
		/// The name to query.
		/// </summary>
		/// <remarks>
		/// Remarks are not rendered
		/// </remarks>
		public string? Name { get; set; }

		/// <summary>
		/// The ID to query
		/// </summary>
		/// <remarks>
		/// Remarks are not rendered
		/// </remarks>
		public int? Id { get; set; }

	}

在SwaggerUI中包括XML注释的Asp.Net Core API。

Is it possible to include remarks using document or schema filters to obtain remarks and append them to the description? I'm using version 6.5.0 of Swashbuckle.AspNetCore

答案1

得分: 0

我的解决方案是复制并修改源XmlComment[...]Filter.cs文件。在查看IncludeXmlComments()的源代码时,我注意到它所做的只是注册过滤器。

public static void IncludeXmlComments(
    this SwaggerGenOptions swaggerGenOptions,
    Func<XPathDocument> xmlDocFactory,
    bool includeControllerXmlComments = false)
{
    var xmlDoc = xmlDocFactory();
    swaggerGenOptions.ParameterFilter<XmlCommentsParameterFilter>(xmlDoc);
    swaggerGenOptions.RequestBodyFilter<XmlCommentsRequestBodyFilter>(xmlDoc);
    swaggerGenOptions.OperationFilter<XmlCommentsOperationFilter>(xmlDoc);
    swaggerGenOptions.SchemaFilter<XmlCommentsSchemaFilter>(xmlDoc);

    if (includeControllerXmlComments)
        swaggerGenOptions.DocumentFilter<XmlCommentsDocumentFilter>(xmlDoc);
}

对我来说,只是不使用预定义的过滤器,而是在Program.cs中注册我本地修改过的副本。我还添加了对外部XML文件的支持。

var apiXmlFilename = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
var apiXmlPath = Path.Combine(AppContext.BaseDirectory, apiXmlFilename);

var xmlFiles = Directory.GetFiles(AppContext.BaseDirectory, "*.xml", SearchOption.TopDirectoryOnly).ToList();
foreach (string xmlFilePath in xmlFiles)
{
    if (string.Compare(apiXmlPath, xmlFilePath, true) == 0)
    {
        // 这些过滤器只适用于此应用程序的XML文档
        options.DocumentFilter<XmlCommentsDocumentFilter>(xmlFilePath);
        options.OperationFilter<XmlCommentsOperationFilter>(xmlFilePath);
    }

    options.ParameterFilter<XmlCommentsParameterFilter>(xmlFilePath);
    options.RequestBodyFilter<XmlCommentsRequestBodyFilter>(xmlFilePath);
    options.SchemaFilter<XmlCommentsSchemaFilter>(xmlFilePath);
}

作为示例,这是我的修改后的模式过滤器,我在摘要中添加了备注:

public class XmlCommentsSchemaFilter : ISchemaFilter
{
    private const string SummaryTag = "summary";
    private const string RemarksTag = "remarks";
    private const string ExampleTag = "example";
    private readonly XPathNavigator _xmlNavigator;

    public XmlCommentsSchemaFilter(string xmlFilePath)
    {
        var xmlDoc = new XPathDocument(xmlFilePath);
        _xmlNavigator = xmlDoc.CreateNavigator();
    }

    public void Apply(OpenApiSchema schema, SchemaFilterContext context)
    {
        if (!string.IsNullOrWhiteSpace(schema.Description))
            return;

        ApplyTypeTags(schema, context.Type);

        if (context.MemberInfo != null)
        {
            ApplyMemberTags(schema, context);
        }
    }

    private void ApplyTypeTags(OpenApiSchema schema, Type type)
    {
        var typeMemberName = XmlCommentsNodeNameHelper.GetMemberNameForType(type);
        var typeSummaryNode = _xmlNavigator.SelectSingleNode($"/doc/members/member[@name='{typeMemberName}']/summary");

        if (typeSummaryNode != null)
        {
            schema.Description = XmlCommentsTextHelper.Humanize(typeSummaryNode.InnerXml);
        }
    }

    private void ApplyMemberTags(OpenApiSchema schema, SchemaFilterContext context)
    {
        var fieldOrPropertyMemberName = XmlCommentsNodeNameHelper.GetMemberNameForFieldOrProperty(context.MemberInfo);
        var fieldOrPropertyNode = _xmlNavigator.SelectSingleNode($"/doc/members/member[@name='{fieldOrPropertyMemberName}']");

        if (fieldOrPropertyNode == null) return;

        var summaryNode = fieldOrPropertyNode.SelectSingleNode(SummaryTag);
        var remarksNode = fieldOrPropertyNode.SelectSingleNode(RemarksTag);

        if (summaryNode != null)
        {
            var summary = XmlCommentsTextHelper.Humanize(summaryNode.InnerXml);
            var remarks = remarksNode != null ? $"<br/><em>{XmlCommentsTextHelper.Humanize(remarksNode.InnerXml)}</em>" : "";

            schema.Description = $"{summary}{remarks}";
        }

        var exampleNode = fieldOrPropertyNode.SelectSingleNode(ExampleTag);
        if (exampleNode != null)
        {
            var exampleAsJson = (schema.ResolveType(context.SchemaRepository) == "string") && !exampleNode.Value.Equals("null")
                    ? $"\"{exampleNode.ToString()}\""
                    : exampleNode.ToString();

            schema.Example = OpenApiAnyFactory.CreateFromJson(exampleAsJson);
        }
    }
}

希望其他人也会找到这个有用。

英文:

My solution was to copy and modify the source XmlComment[...]Filter.cs files. In looking at the source code for IncludeXmlComments(), I noticed that all it did was register the filters.

public static void IncludeXmlComments(
            this SwaggerGenOptions swaggerGenOptions,
            Func&lt;XPathDocument&gt; xmlDocFactory,
            bool includeControllerXmlComments = false)
        {
            var xmlDoc = xmlDocFactory();
            swaggerGenOptions.ParameterFilter&lt;XmlCommentsParameterFilter&gt;(xmlDoc);
            swaggerGenOptions.RequestBodyFilter&lt;XmlCommentsRequestBodyFilter&gt;(xmlDoc);
            swaggerGenOptions.OperationFilter&lt;XmlCommentsOperationFilter&gt;(xmlDoc);
            swaggerGenOptions.SchemaFilter&lt;XmlCommentsSchemaFilter&gt;(xmlDoc);

            if (includeControllerXmlComments)
                swaggerGenOptions.DocumentFilter&lt;XmlCommentsDocumentFilter&gt;(xmlDoc);
        }

It was just a matter of me not using the pre-defined filters and just registering my local modified copies instead within Program.cs. I also added support for external XML files as well.

	var apiXmlFilename = $&quot;{Assembly.GetExecutingAssembly().GetName().Name}.xml&quot;;
	var apiXmlPath = Path.Combine(AppContext.BaseDirectory, apiXmlFilename);

	var xmlFiles = Directory.GetFiles(AppContext.BaseDirectory, &quot;*.xml&quot;, SearchOption.TopDirectoryOnly).ToList();
	foreach (string xmlFilePath in xmlFiles)
	{
		if (string.Compare(apiXmlPath, xmlFilePath, true) == 0)
		{
			// These filters only apply to this application&#39;s XML document
			options.DocumentFilter&lt;XmlCommentsDocumentFilter&gt;(xmlFilePath);
			options.OperationFilter&lt;XmlCommentsOperationFilter&gt;(xmlFilePath);
		}

		options.ParameterFilter&lt;XmlCommentsParameterFilter&gt;(xmlFilePath);
		options.RequestBodyFilter&lt;XmlCommentsRequestBodyFilter&gt;(xmlFilePath);
		options.SchemaFilter&lt;XmlCommentsSchemaFilter&gt;(xmlFilePath);
	}

As an example, here is my modified schema filter where I append remarks to the summary:


	public class XmlCommentsSchemaFilter : ISchemaFilter
	{
		private const string SummaryTag = &quot;summary&quot;;
		private const string RemarksTag = &quot;remarks&quot;;
		private const string ExampleTag = &quot;example&quot;;
		private readonly XPathNavigator _xmlNavigator;

		public XmlCommentsSchemaFilter(string xmlFilePath)
		{
			var xmlDoc = new XPathDocument(xmlFilePath);
			_xmlNavigator = xmlDoc.CreateNavigator();
		}

		public void Apply(OpenApiSchema schema, SchemaFilterContext context)
		{
			if (!string.IsNullOrWhiteSpace(schema.Description))
				return;

			ApplyTypeTags(schema, context.Type);

			if (context.MemberInfo != null)
			{
				ApplyMemberTags(schema, context);
			}
		}

		private void ApplyTypeTags(OpenApiSchema schema, Type type)
		{
			var typeMemberName = XmlCommentsNodeNameHelper.GetMemberNameForType(type);
			var typeSummaryNode = _xmlNavigator.SelectSingleNode($&quot;/doc/members/member[@name=&#39;{typeMemberName}&#39;]/summary&quot;);

			if (typeSummaryNode != null)
			{
				schema.Description = XmlCommentsTextHelper.Humanize(typeSummaryNode.InnerXml);
			}
		}

		private void ApplyMemberTags(OpenApiSchema schema, SchemaFilterContext context)
		{
			var fieldOrPropertyMemberName = XmlCommentsNodeNameHelper.GetMemberNameForFieldOrProperty(context.MemberInfo);
			var fieldOrPropertyNode = _xmlNavigator.SelectSingleNode($&quot;/doc/members/member[@name=&#39;{fieldOrPropertyMemberName}&#39;]&quot;);

			if (fieldOrPropertyNode == null) return;

			var summaryNode = fieldOrPropertyNode.SelectSingleNode(SummaryTag);
			var remarksNode = fieldOrPropertyNode.SelectSingleNode(RemarksTag);

			if (summaryNode != null)
			{
				var summary = XmlCommentsTextHelper.Humanize(summaryNode.InnerXml);
				var remarks = remarksNode != null ? $&quot;&lt;br/&gt;&lt;em&gt;{XmlCommentsTextHelper.Humanize(remarksNode.InnerXml)}&lt;/em&gt;&quot; : &quot;&quot;;

				schema.Description = $&quot;{summary}{remarks}&quot;;
			}

			var exampleNode = fieldOrPropertyNode.SelectSingleNode(ExampleTag);
			if (exampleNode != null)
			{
				var exampleAsJson = (schema.ResolveType(context.SchemaRepository) == &quot;string&quot;) &amp;&amp; !exampleNode.Value.Equals(&quot;null&quot;)
					 ? $&quot;\&quot;{exampleNode.ToString()}\&quot;&quot;
					 : exampleNode.ToString();

				schema.Example = OpenApiAnyFactory.CreateFromJson(exampleAsJson);
			}
		}
	}

Hopefully someone else finds this helpful.

huangapple
  • 本文由 发表于 2023年7月7日 05:33:29
  • 转载请务必保留本文链接:https://go.coder-hub.com/76632646.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定