如何使用 Swashbuckle.AspNetCore 将自定义泛型类型公开为 Swagger 架构中的字符串

Tho*_*que 6 c# swagger swashbuckle asp.net-core swashbuckle.aspnetcore

我有一个自定义泛型类型,大致如下所示:

public struct Foo<T>
{
    public int Value { get; }
    public string Signature { get; }
    public Type Type { get; }
}
Run Code Online (Sandbox Code Playgroud)

该类型用于请求和响应主体以及控制器操作参数。一切都经过配置,以便将其序列化为字符串,并且它可以与模型绑定和 JSON 序列化配合良好。该类型有一个TypeConverter与之关联的对象,它负责将其与字符串相互转换。

然而,Swagger 架构仍然将其表示为具有 3 个属性的对象。该Type属性也被扩展,它拉出System.Reflection直接或间接暴露的所有类型Type

如何避免这种情况并将我的类型公开为字符串?


尝试的第一个解决方案:使用MapType

我尝试使用MapType;如果我指定泛型类型参数,它可以正常工作,但不适用于开放泛型类型:

c.MapType(typeof(Foo<Something>), () => new OpenApiSchema { Type = "string" }); // Works
c.MapType(typeof(Foo<>), () => new OpenApiSchema { Type = "string" }); // Doesn't work
Run Code Online (Sandbox Code Playgroud)

如何将映射应用于Foo<T>, 对于任何T


目前的解决方法

到目前为止,我唯一的解决方法非常丑陋:

class SchemaFilter : ISchemaFilter
{
    public void Apply(OpenApiSchema schema, SchemaFilterContext context)
    {
        if (context.Type is Type type &&
            type.IsGenericType &&
            !type.IsGenericTypeDefinition &&
            type.GetGenericTypeDefinition() == typeof(Foo<>))
        {
            schema.Type = "string";
            schema.Properties.Clear();
        }
        else if (context.Type?.FullName.StartsWith("System.", StringComparison.Ordinal) is true
            && context.SchemaRepository.TryGetIdFor(context.Type, out var schemaId))
        {
            DocFilter.SchemaIdsToRemove.Add(schemaId);
        }
    }
}

class DocFilter : IDocumentFilter
{
    public static readonly HashSet<string> SchemaIdsToRemove = new HashSet<string>();

    public void Apply(OpenApiDocument swaggerDoc, DocumentFilterContext context)
    {
        foreach (var schemaId in SchemaIdsToRemove)
        {
            swaggerDoc.Components.Schemas.Remove(schemaId);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

小智 2

我不确定你想做什么。因为如果我正确理解您的场景,最好的事情就是为 Foo 泛型类型的每个基础类型公开一个架构定义,那么代码将类似于:

public class FooSchemaFilter : ISchemaFilter
    {
        public void Apply(OpenApiSchema schema, SchemaFilterContext context)
        {
            if (context.Type.IsGenericType && context.Type.GetGenericTypeDefinition() == typeof(Foo<>))
            {
                var argumentType = context.Type.GetGenericArguments().First();
                var argumentSchema = context.SchemaGenerator.GenerateSchema(argumentType, context.SchemaRepository);
                var baseSchemaName = $"{argumentType.Name}Foo";
                var baseSchema = new OpenApiSchema()
                {
                    Required = new SortedSet<string>() { "type" },
                    Type = "object",
                    Properties = new Dictionary<string, OpenApiSchema> {
                        { "type", argumentSchema }
                };
                context.SchemaRepository.AddDefinition(baseSchemaName, baseSchema);
                schema.Type = "string";
                schema.Reference = new OpenApiReference { Id = $"{baseSchemaName}", Type = ReferenceType.Schema };
            }
        }
    }
Run Code Online (Sandbox Code Playgroud)

如果您还需要其他属性,请将它们包含在基本架构中。这将为每种类型创建一个新架构,但它应该反序列化为您的泛型类型。