[英]How to expose a custom generic type as a string in Swagger schema using Swashbuckle.AspNetCore
I have a custom generic type that looks roughly like this:我有一个自定义的泛型类型,大致如下所示:
public struct Foo<T>
{
public int Value { get; }
public string Signature { get; }
public Type Type { get; }
}
This type is is used in request and response bodies and in controller action parameters.此类型用于请求和响应主体以及控制器操作参数中。 Everything is configured so that it's serialized as a string, and it works fine with model binding and JSON serialization.一切都经过配置,以便将其序列化为字符串,并且可以很好地与模型绑定和 JSON 序列化配合使用。 The type has a TypeConverter
associated with it, which takes care of converting it to and from a string.该类型有一个与之关联的TypeConverter
,它负责将它与字符串相互转换。
However, the Swagger schema still represents it as an object with 3 properties.但是,Swagger 架构仍将其表示为具有 3 个属性的对象。 The Type
property is also expanded, which pulls all the System.Reflection
types exposed directly or indirectly by Type
. Type
属性也被扩展,它拉取所有System.Reflection
直接或间接暴露的Type
。
How can I avoid this and expose my type as a string?如何避免这种情况并将我的类型公开为字符串?
First solution attempted: using MapType
尝试的第一个解决方案:使用MapType
I tried to use MapType
;我尝试使用MapType
; it works fine if I specify the generic type argument, but doesn't work with the open generic type:如果我指定泛型类型参数,它工作正常,但不适用于开放泛型类型:
c.MapType(typeof(Foo<Something>), () => new OpenApiSchema { Type = "string" }); // Works
c.MapType(typeof(Foo<>), () => new OpenApiSchema { Type = "string" }); // Doesn't work
How can I apply the mapping to Foo<T>
, for any T
?对于任何T
,如何将映射应用于Foo<T>
?
Current workaround当前的解决方法
So far the only workaround I have is pretty ugly :到目前为止,我唯一的解决方法是非常丑陋:
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);
}
}
}
i am not sure what you are trying to do.我不确定你要做什么。 Because the best thing if i understood your scenario correctly would be to expose one schema definition for each underlying type of the Foo generic type, code then would be something like:因为如果我正确理解你的场景,最好的事情就是为 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 };
}
}
}
if you need the other properties as well then include them in the base schema.如果您还需要其他属性,则将它们包含在基本架构中。 This will create a new schema for every type but it should deserialize into your generic type.这将为每种类型创建一个新模式,但它应该反序列化为您的泛型类型。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.