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

96
我正在使用Swashbuckle为webapi2项目生成swagger文档/用户界面。我们的模型与一些旧接口共享,因此有一些属性我想在模型上忽略。我无法使用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属性,这将不会在Swagger中显示该属性。 - Silly John
1
如问题所述,我不想使用JsonIgnore,因为我有需要使用模型的旧代码,如果我应用JsonIgnore,那将影响Swagger和旧代码... - mutex
21个回答

81

如果您需要这样做,但不使用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>();

4
似乎这只适用于输出模型?当我在输入模型上应用此代码(由GET使用),该模型未被找到? - Stef Heyenrath
2
Swashbuckle.AspNetCore.SwaggerGen.ISchemaFilter没有类型参数。如何解决? - Boudewijn van Veen
5
使用这个解决方案时,我遇到了一个大小写敏感的问题。我的 POCO(Plain Old CLR Object)属性名称采用 PascalCase 大小写格式,而序列化对象的名称采用 camelCase 小写格式,因此建议使用 var foundKey = schema.properties.Keys.FirstOrDefault(x => string.Equals(x, excludedProperty.Name, StringComparison.CurrentCultureIgnoreCase)); 进行检查,而不是使用 ContainsKey。 - Attila Bicskó
1
@Richard 这是一个非常有用的答案。我已经发布了更新版本的答案下面,它:适用于最新(v5)版本的Swashbuckle;可以应用于字段和属性;尊重JsonProperty属性可能重命名数据成员。谢谢! - MikeBeaton
2
@Richard 我在 asp.net core 3.1 上尝试了这个解决方案。似乎自定义属性没有拾取 ar excludedProperties = context.Type.GetProperties() .Where(t => t.GetCustomAttribute(typeof(SwaggerExcludeAttribute), true) != null); 这一行始终为空,有什么想法吗? - Gayan
显示剩余3条评论

71

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

10
如果您尝试使用Newtonsoft进行此操作,可能需要安装Swashbuckle.AspNetCore.Newtonsoft nuget包。 - tkit
即使安装了NuGet,仍然找不到... .NET 6。 - jjxtra
哇!这是100%正确的答案。我试图弄清楚这个问题已经两个小时了。 - Waqas Idrees
无法在我的子类中使用SubClass、多态和JsonIgnore属性来覆盖字段,有.NET 6的任何想法吗? - NilkOne
我正在使用.NET 7,但它根本没有任何作用。是的,我三次检查了我没有使用Newtonsoft。将字段设置为“internal”是正确的方法。 - asontu

59

如果你将字段/属性标记为internalprotectedprivate,则在swagger文档中,swashbuckle会自动忽略它。

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


2
这是我个人认为最好的解决方案。 - infl3x
25
这将防止属性从请求体 JSON 中填充。 - adnan kamili
3
没错,不过这种方式非常适用于内部状态等可能不需要在原始请求中使用的属性。并不是说这是一种完美的架构,但它是一个选择。 - jjxtra

37

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

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

1
@mattruma关于驼峰式命名法是正确的。我已经提取了Swashbuckle使用的内部方法并使用了它。我不确定如何在此过滤器的上下文中读取Swashbuckle的当前设置,因为我认为驼峰式命名法可以在某个地方开启或关闭。 - MikeBeaton
根据上面其他答案的说法,我认为这个版本所需的类与@Richard版本的区别在于.NET Core与Framework,而不是Swagger v5与v4。如果有人需要,将其他功能转换回.NET Framework类将相对容易。 - MikeBeaton
1
我正在使用Nswag而不是Swashbuckle。有人知道在Nswag中是否有与“ISchemaFilter”接口相匹配的内容吗? - Silvair L. Soares
6
lib v5.3.3中不存在 schemaFilterContext.SystemType。 - Vedran Mandić
2
"schemaFilterContext.SystemType在lib v5.3.3上不存在,请使用schemaFilterContext.Type代替。" - oflahero
在.NET 6中,针对SubClass、多态和子类覆盖字段上的JsonIgnore属性,对我来说无法正常工作,有什么想法吗? - NilkOne

16

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

2
这似乎不起作用,但我正在使用AspNetCore 2,不知道是否有影响? - mattruma
3
这对我不起作用,因为我的模式是帕斯卡命名法,但是上下文似乎使用的是驼峰命名法。 - ksigmund
已更新以下内容以适用于最新的(v5)Swashbuckle版本,还可以处理字段,还可以使用JsonProperty属性处理字段/属性重命名。 - MikeBeaton

13

对于像我这样正在使用内置的 app.UseSwaggerUi3WithApiExplorer().Net Core 用户

使用 Newtonsoft.Json 中的 [JsonIgnore] 标签。

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

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

这将在您的文档中排除。


1
这样做是否仍允许忽略的属性进行序列化和反序列化,就像OP所要求的那样? - oli_taz
2
我正在使用 .NET Core 3.1,来自 System.Text.Json.Serialization 的 [JsonIgnore] 能够正常工作,但来自 Newtonsoft.Json 则不行! - Shankar
3
尝试使用Newtonsoft进行此操作的人,可能需要安装Swashbuckle.AspNetCore.Newtonsoft nuget包。 - tkit

12

好的,通过一些摸索我找到了使用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。


10
我这里有一个关于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 模式架构中排除的属性上使用自定义属性:
public class MyApiModel
{
    [SwaggerExclude]
    public Guid Token { get; set; }

    public int Id { get; set; }

    public string Name { get; set; }
}

9
你可以使用Swashbuckle.AspNetCore.Annotations包,它允许你标记某些属性仅显示在输入参数中,某些属性仅显示在输出中。
例如,如果您想在post的输入参数中隐藏AlertId,只需使用[SwaggerSchema]
public class Alert
{
    [SwaggerSchema(ReadOnly = true)]
    public string AlertId { get; set; }
    public string Type { get; set; }
}

更多信息请参见文档


1
这对于主键非常有效,当您创建项目时想要隐藏它们,但只有在检索时列出它们。 - Steef

8

基于Stef Heyenrath的答案。

用于标记要从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。调整了方法来将两者转换成小写字母并进行比较以确定应该排除什么。


1
我已经完成了一个版本在这里,采纳了您关于驼峰命名的好建议(谢谢!),但是使用从Swashbuckle复制的ToCamelCase方法实现,同时还支持排除字段和属性,以及使用JsonProperty进行属性重命名的可能性。 - MikeBeaton
2
我通过让SchemaFilter的构造函数接受一个Swashbuckle.AspNetCore.SwaggerGen.ISerializerDataContractResolver实例并将其存储为成员变量来避免属性命名问题,然后使用它来查找类型的序列化属性名称,通过在MemberInfos上进行关联。这样,无论您使用哪个序列化程序或成员是否重命名,都不会有影响。 - Steve Pick

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接