【问题标题】:How to configure Swashbuckle to ignore property on model如何配置 Swashbuckle 以忽略模型上的属性
【发布时间】:2017-04-21 16:53:51
【问题描述】:

我正在使用 Swashbuckle 为 webapi2 项目生成 swagger 文档\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。

【问题讨论】:

  • 您实际上可以使用 JsonIgnore 属性,它不会在招摇中显示该属性
  • 如问题中所述,我不想使用 JsonIgnore,因为我有也需要使用模型的遗留代码,如果我应用 JsonIgnore 会影响招摇和遗留代码......

标签: c# asp.net-web-api swagger swashbuckle


【解决方案1】:

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

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

然后是类似于Johng's的模式过滤器

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>();

【讨论】:

  • 这似乎只适用于输出模型?当我将此代码应用于输入模型(由 GET 使用)时,找不到该模型?
  • Swashbuckle.AspNetCore.SwaggerGen.ISchemaFilter 没有类型参数。那里怎么解决?
  • 我在使用这个区分大小写的解决方案时遇到了问题。我的 POCO 中的属性名称使用 PascalCase,而序列化对象的名称使用 camelCase,因此检查 var foundKey = schema.properties.Keys.FirstOrDefault(x => string.Equals(x) 而不是 ContainsKey 可能是个好主意, excludeProperty.Name, StringComparison.CurrentCultureIgnoreCase));
  • @Richard 这是一个非常有用的答案。我已经发布了它的更新版本below:适用于最新 (v5) 版本的 Swashbuckle;可以应用于字段和属性;尊重JsonProperty 属性可能重命名的数据成员。谢谢!
  • @Richard 我在 asp.net core 3.1 上尝试了这个解决方案,它似乎是自定义属性,没有选择 ar excludedProperties = context.Type.GetProperties() .Where(t =&gt; t.GetCustomAttribute(typeof(SwaggerExcludeAttribute), true) != null); 行总是空的,有什么想法吗?
【解决方案2】:

如果您将字段/属性标记为internalprotectedprivate,它将被swagger 文档中的swashbuckle 自动忽略。

更新:显然,这些属性/字段不会填充到请求/响应中。

【讨论】:

  • 这是 IMO 最好的解决方案
  • 这将阻止从请求正文 json 中填充属性
  • 确实如此,但是这对于原始请求中可能不需要的内部状态内容或其他属性非常有用。并不是说这是一个完美的架构,但它是一种选择。
【解决方案3】:

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

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

(来自Newtonsoft.JsonJsonIgnore 将不起作用)

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

【讨论】:

  • 对于尝试使用 Newtonsoft 进行此操作的任何人,您可能需要安装 Swashbuckle.AspNetCore.Newtonsoft nuget。
  • 安装nuget后也找不到... .NET 6
【解决方案4】:

下面的代码非常基于@Richard 的答案,但我将其作为新答案包含在内,因为它具有我添加的三个全新的有用功能:

  • 在最新版本 Swashbuckle (v5) 上的 .NET Core 上运行
  • 允许将SwaggerIgnore 属性应用于字段而不仅仅是属性
  • 处理属性和字段名称可能已使用 JsonProperty 属性覆盖的事实
  • 编辑:现在可以正确处理最初 TitleCased 字段或属性的 camelCasing(由 @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>();
    ...
});

【讨论】:

  • @mattruma 关于骆驼肠衣的说法是正确的。我已经提取了 Swashbuckle 使用的内部方法并使用了它。我不确定如何在这个过滤器的上下文中读取 Swashbuckle 的当前设置,因为我认为驼色外壳可以在某处打开或关闭。
  • 根据上面的其他几个答案,我认为此版本与@Richard 版本所需类的区别在于 .NET Core 与 Framework,而不是 Swagger v5 与 v4。如果有人需要,将这个版本的其他功能转换回 .NET Framework 类会相对容易。
  • 我使用的是 Nswag 而不是 Swashbuckle。有谁知道 Nswag 中的“ISchemaFilter”接口是否匹配?
  • schemaFilterContext.SystemType 在 lib v5.3.3 上不存在
  • "schemaFilterContext.SystemType 在 lib v5.3.3 上不存在" - 请改用 schemaFilterContext.Type。
【解决方案5】:

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);
            }
        }
    }
}

【讨论】:

  • 这似乎不起作用,但我使用的是 AspNetCore 2,不知道这有什么不同?
  • 这对我不起作用,因为我的架构是 Pascal 大小写,但上下文似乎使用的是驼峰式大小写。
  • 已更新 below 以使用最新 (v5) 版本的 Swashbuckle,还可以处理字段,还可以使用 JsonProperty 属性处理字段/属性重命名。
【解决方案6】:

好吧,经过一番摸索,我找到了一种使用 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 感到好奇。

【讨论】:

    【解决方案7】:

    对于像我这样使用 .Net Core 并使用 app.UseSwaggerUi3WithApiExplorer() 中的构建的人

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

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

    它将从您的文档中排除。

    【讨论】:

    • 尽管按照 OP 的要求,这是否仍然允许对被忽略的属性进行序列化和反序列化?
    • 我使用的是 .net core 3.1,并且 System.Text.Json.Serialization 中的 [JsonIgnore] 有效,但来自 Newtonsoft.Json 的无效!
    • 对于尝试使用 Newtonsoft 进行此操作的任何人,您可能需要安装 Swashbuckle.AspNetCore.Newtonsoft nuget。
    【解决方案8】:

    Based on Stef Heyenrath's answer.

    用于标记要从 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。调整了将两者都转换为小写并比较以查看应该排除的方法。

    【讨论】:

    • 我已经做了一个版本here,它采纳了你关于骆驼肠衣的好观点(谢谢!),但它使用从 Swashbuckle 复制的ToCamelCase 方法,并且还支持排除字段和属性,以及使用JsonProperty 重命名属性的可能性。
    • 我通过让 SchemaFilter 的构造函数获取 Swashbuckle.AspNetCore.SwaggerGen.ISerializerDataContractResolver 的实例,将其存储为成员变量,然后使用它通过关联来查找类型的序列化属性名称,从而避免了属性命名问题会员信息。这样,您使用什么序列化程序或您的成员是否被重命名都无关紧要。
    【解决方案9】:

    我在这里有一个使用 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; }
    }
    

    【讨论】:

      【解决方案10】:

      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();`
      

      【讨论】:

        【解决方案11】:

        这是我在 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);
            }
        }
        

        【讨论】:

          【解决方案12】:

          参考https://stackoverflow.com/a/58193046/11748401的答案,创建过滤器可以简单地使用以下代码:

          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);
                          }
                      }
                  }
              }
          
          }
          

          【讨论】:

            【解决方案13】:

            (Based on mutex's answer.)

            我添加了另一行以确保NullReferenceException 没有问题。

            public void Apply(Schema schema, SchemaRegistry schemaRegistry, Type type)
            {
              var excludeProperties = new[] { "myProp1", "myProp2, myProp3"};
            
               foreach (var prop in excludeProperties)
                 if(schema.properties != null) // This line
                   if (schema.properties.ContainsKey(prop))
                    schema.properties.Remove(prop);        
            }

            如果要删除所有架构

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

            【讨论】:

              【解决方案14】:

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

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

              您应该考虑将它们标记为[Obsolete],而不是让它们没有记录在案。

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

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

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

              【讨论】:

                【解决方案15】:

                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>();
                    });
                
                ......
                
                }
                
                

                【讨论】:

                  【解决方案16】:

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

                  例如,如果你想在帖子的输入参数中隐藏AlertId,你只需要通过[SwaggerSchema]来做到这一点:

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

                  Documentation中查看更多信息

                  【讨论】:

                  • 这对于您想要在创建项目时隐藏它们但仅在检索时列出它们的主键非常有效。
                  【解决方案17】:

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

                  所以,在我的应用层中,我有一个像这样的 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 需要基类 CreateItemCommand。
                  • .Apply 只是我做的一个非常简单的扩展方法,可以轻松地将路由参数值设置到对应的 dto 属性中。

                  【讨论】:

                    【解决方案18】:

                    我需要更多的控制来删除在其他地方声明并且不能轻易使用删除属性的属性。

                    创建的过滤器从我的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));    
                         }
                     }
                    

                    在你的 .Net 6 粉丝的启动或 program.cs 中。

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

                    【讨论】:

                      猜你喜欢
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 2012-06-18
                      • 1970-01-01
                      • 1970-01-01
                      • 2017-08-23
                      相关资源
                      最近更新 更多