gpt4 book ai didi

c# - 将 ISchemaFilter 示例转换为 Swashbuckle 5.0

转载 作者:行者123 更新时间:2023-12-02 11:16:40 27 4
gpt4 key购买 nike

在 Swashbuckle 5 之前,可以定义和注册 ISchemaFilter这可以提供模型的示例实现:

public class MyModelExampleSchemaFilter : ISchemaFilter
{
public void Apply(Schema schema, SchemaFilterContext context)
{
if (context.SystemType.IsAssignableFrom(typeof(MyModel)))
{
schema.Example = new MyModel
{
Name = "model name",
value = 42
};
}
}
}
Schema.Example将采用任意对象,并在生成 OpenApi 架构时正确序列化。

但是,随着迁移到 .NET Core 3 和 Swashbuckle 5, Schema.Example属性(property)不再是 object并且需要类型 Microsoft.OpenApi.Any.IOpenApiAny .似乎没有关于如何提供新示例的文件记录路径。

我尝试过,基于查看 Microsoft.OpenApi 中的代码, 构建我自己的 IOpenApiAny 实现但是任何尝试使用它来生成示例都失败了 Microsoft.OpenApi.Writers.OpenApiWriterAnyExtensions.WriteObject(IOpenApiWriter writer, OpenApiObject entity)在其 Write 之前方法甚至被调用。我并不声称下面的代码是完全正确的,但我希望它至少能照亮一条路径以及如何前进。

/// <summary>
/// A class that recursively adapts a unidirectional POCO tree into an <see cref="IOpenApiAny" />
/// </summary>
/// <remarks>
/// <para>This will fail if a graph is provided (backwards and forwards references</para>
/// </remarks>
public class OpenApiPoco : IOpenApiAny
{
/// <summary>
/// The model to be converted
/// </summary>
private readonly object _model;

/// <summary>
/// Initializes a new instance of the <see cref="OpenApiPoco" /> class.
/// </summary>
/// <param name="model">the model to convert to an <see cref="IOpenApiAny" /> </param>
public OpenApiPoco(object model)
{
this._model = model;
}

/// <inheritdoc />
public AnyType AnyType => DetermineAnyType(this._model);

#region From Interface IOpenApiExtension

/// <inheritdoc />
public void Write(IOpenApiWriter writer,
OpenApiSpecVersion specVersion)
{
this.Write(this._model, writer, specVersion);
}

#endregion

private static AnyType DetermineAnyType(object model)
{
if (model is null)
{
return AnyType.Null;
}

var modelType = model.GetType();

if (modelType.IsAssignableFrom(typeof(int))
|| modelType.IsAssignableFrom(typeof(long))
|| modelType.IsAssignableFrom(typeof(float))
|| modelType.IsAssignableFrom(typeof(double))
|| modelType.IsAssignableFrom(typeof(string))
|| modelType.IsAssignableFrom(typeof(byte))
|| modelType.IsAssignableFrom(typeof(byte[])) // Binary or Byte
|| modelType.IsAssignableFrom(typeof(bool))
|| modelType.IsAssignableFrom(typeof(DateTimeOffset)) // DateTime
|| modelType.IsAssignableFrom(typeof(DateTime)) // Date
)
{
return AnyType.Primitive;
}

if (modelType.IsAssignableFrom(typeof(IEnumerable))) // test after primitive check so as to avoid catching string and byte[]
{
return AnyType.Array;
}

return AnyType.Object; // Assume object
}

private void Write(object model,
[NotNull] IOpenApiWriter writer,
OpenApiSpecVersion specVersion)
{
if (writer is null)
{
throw new ArgumentNullException(nameof(writer));
}

if (model is null)
{
writer.WriteNull();
return;
}

var modelType = model.GetType();

if (modelType.IsAssignableFrom(typeof(int))
|| modelType.IsAssignableFrom(typeof(long))
|| modelType.IsAssignableFrom(typeof(float))
|| modelType.IsAssignableFrom(typeof(double))
|| modelType.IsAssignableFrom(typeof(string))
|| modelType.IsAssignableFrom(typeof(byte[])) // Binary or Byte
|| modelType.IsAssignableFrom(typeof(bool))
|| modelType.IsAssignableFrom(typeof(DateTimeOffset)) // DateTime
|| modelType.IsAssignableFrom(typeof(DateTime)) // Date
)
{
this.WritePrimitive(model, writer, specVersion);
return;
}

if (modelType.IsAssignableFrom(typeof(IEnumerable))) // test after primitive check so as to avoid catching string and byte[]
{
this.WriteArray((IEnumerable) model, writer, specVersion);
return;
}

this.WriteObject(model, writer, specVersion); // Assume object
}

private void WritePrimitive(object model,
IOpenApiWriter writer,
OpenApiSpecVersion specVersion)
{
switch (model.GetType())
{
case TypeInfo typeInfo
when typeInfo.IsAssignableFrom(typeof(string)): // string
writer.WriteValue((string) model);
break;

case TypeInfo typeInfo
when typeInfo.IsAssignableFrom(typeof(byte[])): // assume Binary; can't differentiate from Byte and Binary based on type alone
// if we chose to treat byte[] as Byte we would Base64 it to string. eg: writer.WriteValue(Convert.ToBase64String((byte[]) propertyValue));
writer.WriteValue(Encoding.UTF8.GetString((byte[]) model));
break;

case TypeInfo typeInfo
when typeInfo.IsAssignableFrom(typeof(bool)): // boolean
writer.WriteValue((bool) model);
break;

case TypeInfo typeInfo
when typeInfo.IsAssignableFrom(typeof(DateTimeOffset)): // DateTime as DateTimeOffset
writer.WriteValue((DateTimeOffset) model);
break;

case TypeInfo typeInfo
when typeInfo.IsAssignableFrom(typeof(DateTime)): // Date as DateTime
writer.WriteValue((DateTime) model);
break;

case TypeInfo typeInfo
when typeInfo.IsAssignableFrom(typeof(double)): // Double
writer.WriteValue((double) model);
break;

case TypeInfo typeInfo
when typeInfo.IsAssignableFrom(typeof(float)): // Float
writer.WriteValue((float) model);
break;

case TypeInfo typeInfo
when typeInfo.IsAssignableFrom(typeof(int)): // Integer
writer.WriteValue((int) model);
break;

case TypeInfo typeInfo
when typeInfo.IsAssignableFrom(typeof(long)): // Long
writer.WriteValue((long) model);
break;

case TypeInfo typeInfo
when typeInfo.IsAssignableFrom(typeof(Guid)): // Guid (as a string)
writer.WriteValue(model.ToString());
break;

default:
throw new ArgumentOutOfRangeException(nameof(model),
model?.GetType()
.Name,
"unexpected model type");
}
}

private void WriteArray(IEnumerable model,
IOpenApiWriter writer,
OpenApiSpecVersion specVersion)
{
writer.WriteStartArray();

foreach (var item in model)
{
this.Write(item, writer, specVersion); // recursive call
}

writer.WriteEndArray();
}

private void WriteObject(object model,
IOpenApiWriter writer,
OpenApiSpecVersion specVersion)
{
var propertyInfos = model.GetType()
.GetProperties();

writer.WriteStartObject();

foreach (var property in propertyInfos)
{
writer.WritePropertyName(property.Name);

var propertyValue = property.GetValue(model);

switch (propertyValue.GetType())
{
case TypeInfo typeInfo // primitives
when typeInfo.IsAssignableFrom(typeof(string)) // string
|| typeInfo.IsAssignableFrom(typeof(byte[])) // assume Binary or Byte
|| typeInfo.IsAssignableFrom(typeof(bool)) // boolean
|| typeInfo.IsAssignableFrom(typeof(DateTimeOffset)) // DateTime as DateTimeOffset
|| typeInfo.IsAssignableFrom(typeof(DateTime)) // Date as DateTime
|| typeInfo.IsAssignableFrom(typeof(double)) // Double
|| typeInfo.IsAssignableFrom(typeof(float)) // Float
|| typeInfo.IsAssignableFrom(typeof(int)) // Integer
|| typeInfo.IsAssignableFrom(typeof(long)) // Long
|| typeInfo.IsAssignableFrom(typeof(Guid)): // Guid (as a string)
this.WritePrimitive(propertyValue, writer, specVersion);
break;

case TypeInfo typeInfo // Array test after primitive check so as to avoid catching string and byte[]
when typeInfo.IsAssignableFrom(typeof(IEnumerable)): // Enumerable as array of objects
this.WriteArray((IEnumerable) propertyValue, writer, specVersion);
break;

case TypeInfo typeInfo // object
when typeInfo.IsAssignableFrom(typeof(object)): // Object
default:
this.Write(propertyValue, writer, specVersion); // recursive call
break;
}
}

writer.WriteEndObject();
}
}

过渡的正确方式是什么 ISchemaFilter Swashbuckle 5.0 的示例,以便遵守适当的序列化规则?

最佳答案

他们在 repo 上有一个例子:
https://github.com/domaindrivendev/Swashbuckle.AspNetCore/blob/9bb9be9b318c576d236152f142aafa8c860fb946/test/WebSites/Basic/Swagger/ExamplesSchemaFilter.cs#L8

public class ExamplesSchemaFilter : ISchemaFilter
{
public void Apply(OpenApiSchema schema, SchemaFilterContext context)
{
schema.Example = GetExampleOrNullFor(context.Type);
}

private IOpenApiAny GetExampleOrNullFor(Type type)
{
switch (type.Name)
{
case "Product":
return new OpenApiObject
{
[ "id" ] = new OpenApiInteger(123),
[ "description" ] = new OpenApiString("foobar"),
[ "price" ] = new OpenApiDouble(14.37)
};
default:
return null;
}
}
}

关于c# - 将 ISchemaFilter 示例转换为 Swashbuckle 5.0,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60515825/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com