c# - 为什么 autorest 用 Swagger 中的对象替换我的自定义结构?
问题描述
我创建了一个自定义readonly
struct
来定义我称之为的不可变值类型TenantId
:
[DebuggerDisplay("ID={m_internalId.ToString()}")]
[JsonConverter(typeof(TenantIdJsonConverter))]
public readonly struct TenantId : IEquatable<TenantId>
{
private readonly Guid m_internalId;
public static TenantId New => new(Guid.NewGuid());
private TenantId(Guid id)
{
m_internalId = id;
}
public TenantId(TenantId otherTenantId)
{
m_internalId = otherTenantId.m_internalId;
}
...
}
我还定义了一个名为的合同PurchaseContract
,它是 HTTP 响应的一部分:
[JsonObject(MemberSerialization.OptIn)]
public sealed class PurchaseContract
{
[JsonProperty(PropertyName = "tenantId")]
public TenantId TenantId { get; }
[JsonProperty(PropertyName = "total")]
public double Total { get; }
}
最后,我设置了一个 HTTP 触发函数,该函数将返回一个PurchaseContract
. 目前,它已在以下内容中进行了描述ProducesResponseTypeAttribute
:
[ApiExplorerSettings(GroupName = "Purchases")]
[ProducesResponseType(typeof(PurchaseContract), (int) HttpStatusCode.OK)]
[FunctionName("v1-get-purchase")]
public Task<IActionResult> RunAsync
(
[HttpTrigger(AuthorizationLevel.Anonymous, "GET", Route = "v1/purchases")]
HttpRequest httpRequest,
[SwaggerIgnore]
ClaimsPrincipal claimsPrincipal
)
{
// Stuff to do.
return Task.FromResult((IActionResult)new OkResult());
}
在我的Startup
课堂上,我正在设置这样的招摇:
private static void ConfigureSwashBuckle(IFunctionsHostBuilder functionsHostBuilder)
{
functionsHostBuilder.AddSwashBuckle(Assembly.GetExecutingAssembly(), options =>
{
options.SpecVersion = OpenApiSpecVersion.OpenApi3_0;
options.AddCodeParameter = true;
options.PrependOperationWithRoutePrefix = true;
options.XmlPath = "FunctionApp.xml";
options.Documents = new []
{
new SwaggerDocument
{
Title = "My API,
Version = "v1",
Name = "v1",
Description = "Description of my API",
}
};
});
}
在 swagger UI 页面中,我可以看到它看起来不错:
问题
使用 Autorest 创建 C# 客户端时出现意外结果。不知何故,该TenantId
结构被删除并替换为object
:
为什么会这样,我应该怎么做才能自动生成TenantId
,就像PurchaseContract
在客户端一样?
细节
这是版本信息。
- 在 netcore3.1 上运行的 Function App V3;
- 开放API 3.0;
- Autorest Core 3.0.6274、autorest.csharp' (~2.3.79->2.3.91) 和 autorest.modeler' (2.3.55->2.3.55);
- NuGet 包AzureExtensions.Swashbuckle;
解决方案
我开始研究Swashbuckle.AspNetCore.SwaggerGen的源代码,以了解我readonly
struct
的解释方式。这一切都发生在JsonSerializerDataContractResolver类中,在GetDataContractForType
确定DataContract
所提供类型的方法中:
public DataContract GetDataContractForType(Type type)
{
if (type.IsOneOf(typeof(object), typeof(JsonDocument), typeof(JsonElement)))
{
...
}
if (PrimitiveTypesAndFormats.ContainsKey(type))
{
...
}
if (type.IsEnum)
{
...
}
if (IsSupportedDictionary(type, out Type keyType, out Type valueType))
{
...
}
if (IsSupportedCollection(type, out Type itemType))
{
...
}
return DataContract.ForObject(
underlyingType: type,
properties: GetDataPropertiesFor(type, out Type extensionDataType),
extensionDataType: extensionDataType,
jsonConverter: JsonConverterFunc);
}
我的习惯struct
TenantId
与这些条件中的任何一个都不匹配,因此,它回退到被视为object
(最后一个陈述)。
然后我继续查看现有的测试,看看这个类是如何使用的,看看我是否可以改变任何东西。令人惊讶的是,我发现了一个名为GenerateSchema_SupportsOption_CustomTypeMappings
(第 356 行)的测试,它显示了一种提供自定义映射的方法(参见该方法的第一个语句):
[Theory]
[InlineData(typeof(ComplexType), typeof(ComplexType), "string")]
[InlineData(typeof(GenericType<int, string>), typeof(GenericType<int, string>), "string")]
[InlineData(typeof(GenericType<,>), typeof(GenericType<int, int>), "string")]
public void GenerateSchema_SupportsOption_CustomTypeMappings(
Type mappingType,
Type type,
string expectedSchemaType)
{
var subject = Subject(configureGenerator: c => c.CustomTypeMappings.Add(mappingType, () => new OpenApiSchema { Type = "string" }));
var schema = subject.GenerateSchema(type, new SchemaRepository());
Assert.Equal(expectedSchemaType, schema.Type);
Assert.Empty(schema.Properties);
}
就我而言,我希望将 myTenantId
映射到string
. 为此,我在 Function App 启动时编辑了 SwashBuckle 的配置:
private static void ConfigureSwashBuckle(IFunctionsHostBuilder functionsHostBuilder)
{
functionsHostBuilder.AddSwashBuckle(Assembly.GetExecutingAssembly(), options =>
{
...
options.ConfigureSwaggerGen = (swaggerGenOptions) => swaggerGenOptions.MapType<TenantId>(() => new OpenApiSchema {Type = "string"});
});
}
在这里,TenantId
现在被认为是string
Swagger 中的 a。
推荐阅读
- javascript - 使用 Query 的赋值运算符排除 Mongoose 中的字段
- javascript - 如何监听其他元素的事件
- css - 使用 react-bootstrap 在一定高度后切断内容
- php - 在 PHP 中什么都不返回时如何解决“$result->property”
- selenium - 如何在同一浏览器的两个选项卡中打开两个不同的 url 并在它们之间切换
- python-2.x - 有没有办法让我的程序保存正在附加的东西,所以即使程序启动它总是在列表中?
- arrays - 在 Python 3 中创建排行榜,排行榜必须从高到低排序
- java - Spring表单中如何指定默认选择哪个值:select
- python - Python:TaggedCorpusReader 如何从 STTS 获取通用标签集
- python - 如何运行具有绝对导入的子目录中的python脚本