如何配置 Swashbuckle 以忽略模型上的 属性

How to configure Swashbuckle to ignore property on model

我正在使用 Swashbuckle 为 webapi2 项目生成 swagger documentation\UI。我们的模型与一些遗留接口共享,因此我想在模型上忽略几个属性。我不能使用 JsonIgnore 属性,因为遗留接口也需要序列化为 JSON 所以我不想全局忽略这些属性,只是在 Swashbuckle 配置中。

我在此处找到了执行此操作的方法:

https://github.com/domaindrivendev/Swashbuckle/issues/73

但这对于当前的 Swashbuckle 版本来说似乎已经过时了。

推荐用于旧版本 Swashbuckle 的方法是使用 IModelFilter 实现,如下所示:

public class OmitIgnoredProperties : IModelFilter
{
    public void Apply(DataType model, DataTypeRegistry dataTypeRegistry, Type type)
    {
        var ignoredProperties = … // use reflection to find any properties on 
                                  // type decorated with the ignore attributes

        foreach (var prop in ignoredProperties) 
            model.Properties.Remove(prop.Name);

    }
}

SwaggerSpecConfig.Customize(c => c.ModelFilter<OmitIgnoredProperties>());

但我不确定如何配置 Swashbuckle 以在当前版本中使用 IModelFilter?我正在使用 Swashbuckle 5.5.3。

好吧,经过一番摸索,我找到了使用 ISchemaFilter 执行此操作的方法:

public class ApplyCustomSchemaFilters : ISchemaFilter
{
    public void Apply(Schema schema, SchemaRegistry schemaRegistry, Type type)
    {
        var excludeProperties = new[] {"myProp1", "myProp2", "myProp3"};

        foreach(var prop in excludeProperties)
            if (schema.properties.ContainsKey(prop))
                schema.properties.Remove(prop);
    }
}

然后在调用 httpConfiguration.EnableSwagger 时,我将 SwaggerDocsConfig 设置为使用此 SchemaFilter,如下所示:

c.SchemaFilter<ApplyCustomSchemaFilters>();

希望这对某人有所帮助。不过,我仍然很好奇是否可以以某种方式使用 IModelFilter。

(.)

我添加了另一行以避免 NullReferenceException 出现问题。

public void Apply(Schema schema, SchemaRegistry schemaRegistry, Type type)
{
  var excludeProperties = new[] { "myProp1", "myProp2, myProp3"};

   foreach (var prop in excludeProperties)
    <b><i> if(schema.properties != null) // This line</i></b>
       if (schema.properties.ContainsKey(prop))
        schema.properties.Remove(prop);        
}

如果要删除所有架构

public void Apply(Schema schema, SchemaRegistry schemaRegistry, Type type)
{
  schema.properties = null;       
} 

这是我在 Newtonsoft.Json.JsonIgnoreAttribute 中使用的内容:

internal class ApplySchemaVendorExtensions : Swashbuckle.Swagger.ISchemaFilter
{
    public void Apply(Schema schema, SchemaRegistry schemaRegistry, Type type)
    {
        foreach (var prop in type.GetProperties(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance)
                                 .Where(p => p.GetCustomAttributes(typeof(Newtonsoft.Json.JsonIgnoreAttribute), true)?.Any() == true))
            if (schema?.properties?.ContainsKey(prop.Name) == true)
                schema?.properties?.Remove(prop.Name);
    }
}

如果您将 field/property 标记为 internalprotectedprivate,它会被 swagger 文档中的 swashbuckle 自动忽略。

更新:显然,那些 properties/fields 不会填充到 request/response.

如果您需要这样做但不使用 JsonIgnore(也许您仍然需要 serialize/deserialize 属性),那么只需创建一个自定义属性即可。

[AttributeUsage(AttributeTargets.Property)]
public class SwaggerExcludeAttribute : Attribute
{
}

然后是类似于

的架构过滤器
public class SwaggerExcludeFilter : ISchemaFilter
{
    #region ISchemaFilter Members

    public void Apply(Schema schema, SchemaRegistry schemaRegistry, Type type)
    {
        if (schema?.properties == null || type == null)
            return;

        var excludedProperties = type.GetProperties()
                                     .Where(t => 
                                            t.GetCustomAttribute<SwaggerExcludeAttribute>() 
                                            != null);

        foreach (var excludedProperty in excludedProperties)
        {
            if (schema.properties.ContainsKey(excludedProperty.Name))
                schema.properties.Remove(excludedProperty.Name);
        }
    }

    #endregion
}

别忘了注册过滤器

c.SchemaFilter<SwaggerExcludeFilter>();

AspNetCore 解决方案如下所示:

public class SwaggerExcludeSchemaFilter : ISchemaFilter
{
    public void Apply(Schema schema, SchemaFilterContext context)
    {
        if (schema?.Properties == null)
        {
            return;
        }

        var excludedProperties = context.SystemType.GetProperties().Where(t => t.GetCustomAttribute<SwaggerExcludeAttribute>() != null);
        foreach (PropertyInfo excludedProperty in excludedProperties)
        {
            if (schema.Properties.ContainsKey(excludedProperty.Name))
            {
                schema.Properties.Remove(excludedProperty.Name);
            }
        }
    }
}

用于标记要从 Swagger 文档中排除的属性的属性。

[AttributeUsage(AttributeTargets.Property)]
public class SwaggerExcludeAttribute : Attribute
{
}

从 Swagger 文档中排除属性的过滤器。

public class SwaggerExcludeSchemaFilter : ISchemaFilter
{
    public void Apply(Schema schema, SchemaFilterContext context)
    {
        if (schema?.Properties == null)
        {
            return;
        }

        var excludedProperties = 
            context.SystemType.GetProperties().Where(
                t => t.GetCustomAttribute<SwaggerExcludeAttribute>() != null);

        foreach (var excludedProperty in excludedProperties)
        {
            var propertyToRemove =
                schema.Properties.Keys.SingleOrDefault(
                    x => x.ToLower() == excludedProperty.Name.ToLower());

            if (propertyToRemove != null)
            {
                schema.Properties.Remove(propertyToRemove);
            }
        }
    }
}

schema.Properties.KeyscamelCase,而属性本身是 PascalCase。调整方法以将两者都转换为小写并进行比较以查看应排除的内容。

对于像我这样使用 .Net Core 并使用 app.UseSwaggerUi3WithApiExplorer()

中的构建的人

使用 [JsonIgnore] 标签使用 Newtonsoft.Json;

public class Project
{
    [Required]
    public string ProjectName { get; set; }

    [JsonIgnore]
    public string SomeValueYouWantToIgnore { get; set; }
}

它将从您的文档中排除。

下面的代码很大程度上基于@Richard 的回答,但我将它作为一个新答案包括在内,因为它具有我添加的三个全新的、有用的功能:

  • 在最新版本的 Swashbuckle (v5) 上的 .NET Core 上运行
  • 允许 SwaggerIgnore 属性应用于字段而不仅仅是属性
  • 处理 属性 和字段名称可能已使用 JsonProperty 属性覆盖的事实
  • 编辑:现在可以正确处理原始 TitleCased 字段或属性的驼峰式大小写(@mattruma 的回答提示)

所以修改后的代码是:

[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
public class SwaggerIgnoreAttribute : Attribute
{
}
internal static class StringExtensions
{
    internal static string ToCamelCase(this string value)
    {
        if (string.IsNullOrEmpty(value)) return value;
        return char.ToLowerInvariant(value[0]) + value.Substring(1);
    }
}
public class SwaggerIgnoreFilter : ISchemaFilter
{
    public void Apply(OpenApiSchema schema, SchemaFilterContext schemaFilterContext)
    {
        if (schema.Properties.Count == 0)
            return;

        const BindingFlags bindingFlags = BindingFlags.Public |
                                          BindingFlags.NonPublic |
                                          BindingFlags.Instance;
        var memberList = schemaFilterContext.SystemType // In v5.3.3+ use Type instead
                            .GetFields(bindingFlags).Cast<MemberInfo>()
                            .Concat(schemaFilterContext.SystemType // In v5.3.3+ use Type instead
                            .GetProperties(bindingFlags));

        var excludedList = memberList.Where(m =>
                                            m.GetCustomAttribute<SwaggerIgnoreAttribute>()
                                            != null)
                                     .Select(m =>
                                         (m.GetCustomAttribute<JsonPropertyAttribute>()
                                          ?.PropertyName
                                          ?? m.Name.ToCamelCase()));

        foreach (var excludedName in excludedList)
        {
            if (schema.Properties.ContainsKey(excludedName))
                schema.Properties.Remove(excludedName);
        }
    }
}

并在 Startup.cs 中:

services.AddSwaggerGen(c =>
{
    ...
    c.SchemaFilter<SwaggerIgnoreFilter>();
    ...
});

我这里有一个使用 DotNetCore 3 和 Swashbuckle 5 的工作示例。我花了几个小时才准备好它,所以我想回到这个对我有帮助但没有解决我的问题的线程。

创建虚拟自定义属性:

[AttributeUsage(AttributeTargets.Property)]
public class SwaggerExcludeAttribute : Attribute { }

创建一个 SchemaFilter,swagger 将使用它来生成 API 模型架构

public class SwaggerExcludeFilter : ISchemaFilter
{
    public void Apply(OpenApiSchema schema, SchemaFilterContext context)
    {
        if (!(context.ApiModel is ApiObject))
        {
            return;
        }

        var model = context.ApiModel as ApiObject;

        if (schema?.Properties == null || model?.ApiProperties == null)
        {
            return;
        }
        var excludedProperties = model.Type
                .GetProperties()
                .Where(
                    t => t.GetCustomAttribute<SwaggerExcludeAttribute>() != null
                );

        var excludedSchemaProperties = model.ApiProperties
               .Where(
                    ap => excludedProperties.Any(
                        pi => pi.Name == ap.MemberInfo.Name
                    )
                );

        foreach (var propertyToExclude in excludedSchemaProperties)
        {
            schema.Properties.Remove(propertyToExclude.ApiName);
        }
    }
}

然后,在 Startup.cs 文件中将其添加到 swagger 配置中

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

您现在可以在要从 API 模式 Shema 中排除的 属性 使用自定义属性

public class MyApiModel
{
    [SwaggerExclude]
    public Guid Token { get; set; }

    public int Id { get; set; }

    public string Name { get; set; }
}

参考答案,创建过滤器只需使用以下代码:

public class SwaggerExcludeFilter : ISchemaFilter
{
    public void Apply(OpenApiSchema model, SchemaFilterContext context)
    {

        var excludeProperties = context.ApiModel.Type?.GetProperties().Where(prop => Attribute.IsDefined(prop, typeof(SwaggerExcludeAttribute)));
        if (excludeProperties != null)
        {
            foreach (var property in excludeProperties)
            {
                // Because swagger uses camel casing
                var propertyName = $"{ToLowerInvariant(property.Name[0])}{property.Name.Substring(1)}";
                if (model.Properties.ContainsKey(propertyName))
                {
                    model.Properties.Remove(propertyName);
                }
            }
        }
    }

}

.NET Core 3.1.NET Standard 2.1 的解决方案:

使用 System.Text.Json.Serialization 命名空间中的 JsonIgnore

Newtonsoft.Json 中的 JsonIgnore 将不起作用)

public class Test
{
    [System.Text.Json.Serialization.JsonIgnore]
    public int HiddenProperty { get; set; }
    public int VisibleProperty { get; set; }
}

Swashbuckle 现在支持 Newtonsoft。 https://github.com/domaindrivendev/Swashbuckle.AspNetCore#systemtextjson-stj-vs-newtonsoft

dotnet add package --version 5.3.1 Swashbuckle.AspNetCore.Newtonsoft

`services.AddSwaggerGenNewtonsoftSupport(); // explicit opt-in - needs tobe placed after AddSwaggerGen();`

在我的例子中,我想保持我的应用层 DTO 干净(没有像 JsonIngore 这样的任何注释)但仍然能够在我的控制器 Web APIs 中使用它们。

所以,在我的应用程序层中,我有一个这样的 DTO:

public class CreateItemCommand {
     public Guid ContainerId { get; set; }
     public string Name { get; set; }
}

我的 API 创建项目的设计是这样的: POST /containers/{containerId}/items

由于 ContainerId 来自 api 路由,我不希望 asp.net 核心试图将它绑定到命令 DTO 中,我也不希望 swashbuckle 列出它。

所以我的解决方案是像这样继承API层中的原始DTO:

public class CreateItemCommandMod : CreateItemCommand {
   #pragma warning disable IDE0051
   private new ContainerID { get; }
   #pragma warning restore IDE0051
}

...

[HttpPost("{containerId}/items}")]
public Task Create(
   [FromRoute] Guid containerId,
   [FromBody] CreateItemCommandMod command,
) => useCase.Create(command.Apply(r => r.ContainerId = containerId));
  • ApplicationLayer 中的 useCase.Create 需要基础 class CreateItemCommand。
  • .apply只是我做的一个很简单的扩展方法,可以方便的将路由参数值设置到对应的dto中属性.

我从 Ignoring properties from controller action model in Swagger using JsonIgnore 的博客中得到启发。

我正在使用 .net core 2.1Swashbuckle.AspNetCore 5.3.1。 下面的代码解决了这个问题。

添加新过滤器

public class SwaggerJsonIgnoreFilter : IOperationFilter
    {
        public void Apply(OpenApiOperation operation, OperationFilterContext context)
        {
            var ignoredProperties = context.MethodInfo.GetParameters()
                .SelectMany(p => p.ParameterType.GetProperties()
                .Where(prop => prop.GetCustomAttribute<JsonIgnoreAttribute>() != null))
                .ToList();

            if (!ignoredProperties.Any()) return;

            foreach (var property in ignoredProperties)
            {
                operation.Parameters = operation.Parameters
                    .Where(p => (!p.Name.Equals(property.Name, StringComparison.InvariantCulture)))
                    .ToList();
            }
        }
    }

使用 Startup.cs

中的过滤器
public void ConfigureServices(IServiceCollection services)
{

......

    services.AddSwaggerGen(options =>
    {
        options.SwaggerDoc("v1", new OpenApiInfo { Title = "CustomApi", Version = "v1" });
        options.OperationFilter<SwaggerJsonIgnoreFilter>();
    });

......

}

这是一个较旧的问题,但此后在 Swashbuckle 中提供了一种省力的中间解决方案。

从文档中隐藏遗留属性并不会阻止对这些属性的使用——它只会延迟发现。毕竟,它们仍然是模型的一部分。事实上,让它们没有记录意味着消费者无法知道他们不应该使用它们!

与其让它们无文档记录,不如简单地考虑将它们标记为 [Obsolete]

Swashbuckle 将在 swagger.json 中将它们标记为已弃用。在 UI 中,这会将它们隐藏在“示例值”部分中,而在“架构”部分中,它们将显示为灰色,并在名称上添加删除线。

如果您仍然希望它们在文档中完全隐藏,您可以在 SwaggerGeneratorOptions.IgnoreObsoleteProperties = true.

中设置

最初提出此问题时,这不是可行的解决方案。 已弃用 标志是 OpenAPI v3 的一项功能,直到 2017 年才发布。

你可以使用Swashbuckle.AspNetCore.Annotations包,它允许你标记一些属性只在输入参数中显示,一些属性只在输出中显示。

例如,如果你想隐藏post的输入参数中的AlertId,你只需要通过[SwaggerSchema]:

public class Alert
{
    [SwaggerSchema(ReadOnly = true)]
    public string AlertId { get; set; }
    public string Type { get; set; }
}

Documentation

中查看更多信息

我需要更多的控制来删除在别处声明的属性,并且不能轻易使用删除属性。

创建的过滤器删除了它从我的 excludes 列表中遇到的所有项目:

public class SwaggerExcludeFilter : ISchemaFilter
{
    private static readonly List<string> excludes = new List<string>()
    {
       "StoredProcedureName", "ValidationErrors", "changeTracker",
       "code", "customerId", "IsDebug",
    };

    public void Apply(OpenApiSchema schema, SchemaFilterContext context)
    {
        if (schema?.Properties == null || context == null)
            return;

        // Find all properties by name which need to be removed 
       // and not shown on the swagger spec.
        schema.Properties
              .Where(prp => excludes.Any(exc => string.Equals(exc, prp.Key, StringComparison.OrdinalIgnoreCase)))
              .Select(prExclude => prExclude.Key)
              .ToList()
              .ForEach(key => schema.Properties.Remove(key));    
     }
 }

正在启动或 program.cs .Net 6 粉丝。

services.AddSwaggerGen(c =>
                {
                    c.SwaggerDoc("v1", new Info
                    {
                        Version = "2.5",
                        Title = "My Swagger Doc G",
                    });

                    c.SchemaFilter<SwaggerExcludeFilter>();     
                    ...