本文介绍了将IShemaFilter示例过渡到Swashbakle 5.0的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!
问题描述
在Swashbakle 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和Swashbakle 5的迁移,Schema.Example
属性不再是object
,需要类型Microsoft.OpenApi.Any.IOpenApiAny
。对于如何提供新的示例,似乎没有记录在案的前进路径。
Microsoft.OpenApi
中的代码,我尝试构建我自己的IOpenApiAny
实现,但在调用Write
方法之前,任何使用它来生成示例的尝试都会在Microsoft.OpenApi.Writers.OpenApiWriterAnyExtensions.WriteObject(IOpenApiWriter writer, OpenApiObject entity)
中失败。我不认为下面的代码是完全正确的,但我希望它至少能照亮一条道路以及如何前进。
/// <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
示例转换到Swashbakle 5.0以便遵守相应的序列化规则的正确方法是什么?
推荐答案
他们有一个关于回购的示例:
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;
}
}
}
这篇关于将IShemaFilter示例过渡到Swashbakle 5.0的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持编程学习网!
本站部分内容来源互联网,如果有图片或者内容侵犯您的权益请联系我们删除!