在生成 swagger 文件时,如何让 Swashbuckle 尊重数据类型?
How can I make Swashbuckle respect DataType when generating swagger files?
我正在使用以下数据协定的更复杂版本,但这足以作为示例:
using System;
using System.Runtime.Serialization;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
[DataContract(Namespace = "https://schemas.company.name/api-name/")]
public class Subscription
{
[DataMember(IsRequired = true)]
public long SubscriptionID { get; set; }
[DataMember(IsRequired = true)]
public long ProductID { get; set; }
[DataMember(IsRequired = true)]
public long AccountID { get; set; }
[DataMember(IsRequired = true), DataType(DataType.Date)]
public DateTime StartDate { get; set; }
[DataMember(IsRequired = true), DataType(DataType.Date)]
public DateTime EndDate { get; set; }
}
Swashbuckle 为上述数据契约生成的 swagger JSON 定义变成了这样:
...
"Subscription": {
"required": ["subscriptionID", "productID", "accountID", "startDate", "endDate"],
"type": "object",
"properties": {
"subscriptionID": {
"format": "int64",
"type": "integer"
},
"productID": {
"format": "int64",
"type": "integer"
},
"accountID": {
"format": "int64",
"type": "integer"
},
"startDate": {
"format": "date-time",
"type": "string"
},
"endDate": {
"format": "date-time",
"type": "string"
}
}
},
...
但是,您会注意到 JSON definitions.Subscription.properties.startDate.format
是 "date-time"
,但 C# 代码中的 DateTypeAttribute
注释是 DataType.Date
,而不是 DataType.DateTime
.
如何在生成 swagger 文件时让 Swashbuckle 尊重 System.ComponentModel.DataAnnotations.DataTypeAttribute
?或者更具体地说,使 [DataType(DataType.Date]
注释的 class 属性生成 format
of "date"
?
的 swagger format
我希望这是所有 classes 的默认行为,因为我有太多硬编码特定 class 属性的详细信息,这就是使用 Swashbuckle 生成的全部意义swagger JSON 基于同一命名空间内的其他注释(例如 System.ComponentModel.DataAnnotations.StringLengthAttribute
)。
我最初的尝试是尝试在我的 Startup.cs 中使用 ISchemaFilter,例如:
services.AddSwaggerGen(options =>
{
...
options.SchemaFilter<DataTypeSchemaFilter>();
...
});
实施过滤器 class 的位置 应用:
public class DataTypeSchemaFilter : ISchemaFilter
{
public void Apply(Schema model, SchemaFilterContext context)
{
???
}
}
但是,我发现无法使用提供的 Schema model
和 SchemaFilterContext context
参数从过滤器中调查 class 属性 属性。
如前所述,我知道 Swashbuckle 在处理 class 属性时会查看同一命名空间内的属性,因此我希望有人知道我可以在哪里绑定到 Swashbuckle 并执行类似的任务。
我写了下面的代码。从本质上讲,这个想法是将 class 上的 属性 名称连接到模式 属性 名称 (schema.properties)。鉴于您可能有自定义序列化程序设置(Camel Case),属性 名称的大小写在模式中可能与 class 中定义的不同。
我在 SetSchemaDetails 方法中也包含了父架构,以便您可以根据需要在父级添加属性。我们有偶尔使用的自定义必需属性,因此我们需要在父(包含 class)架构级别而不是 属性 架构级别指定必需属性。
public class DataAnnotationSchemaFilter : ISchemaFilter
{
public void Apply(Schema schema, SchemaFilterContext schemaFilterContext)
{
var type = schemaFilterContext.SystemType;
var propertyMappings = type
.GetProperties()
.Join(
schema.Properties ?? new Dictionary<string, Schema>(),
x => x.Name.ToLower(),
x => x.Key.ToLower(),
(x, y) => new KeyValuePair<PropertyInfo, KeyValuePair<string, Schema>>(x, y))
.ToList();
foreach (var propertyMapping in propertyMappings)
{
var propertyInfo = propertyMapping.Key;
var propertyNameToSchemaKvp = propertyMapping.Value;
foreach (var attribute in propertyInfo.GetCustomAttributes())
{
SetSchemaDetails(schema, propertyNameToSchemaKvp, propertyInfo, attribute);
}
}
}
private static void SetSchemaDetails(Schema parentSchema, KeyValuePair<string, Schema> propertyNameToSchemaKvp, PropertyInfo propertyInfo, object propertyAttribute)
{
var schema = propertyNameToSchemaKvp.Value;
if (propertyAttribute is DataTypeAttribute)
{
var dataType = ((DataTypeAttribute)propertyAttribute).DataType;
if (dataType == DataType.Date)
{
schema.Format = "date";
schema.Type = "date";
}
}
if (propertyAttribute is ReadOnlyAttribute)
{
schema.ReadOnly = ((ReadOnlyAttribute)propertyAttribute).IsReadOnly;
}
}
}
我正在使用以下数据协定的更复杂版本,但这足以作为示例:
using System;
using System.Runtime.Serialization;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
[DataContract(Namespace = "https://schemas.company.name/api-name/")]
public class Subscription
{
[DataMember(IsRequired = true)]
public long SubscriptionID { get; set; }
[DataMember(IsRequired = true)]
public long ProductID { get; set; }
[DataMember(IsRequired = true)]
public long AccountID { get; set; }
[DataMember(IsRequired = true), DataType(DataType.Date)]
public DateTime StartDate { get; set; }
[DataMember(IsRequired = true), DataType(DataType.Date)]
public DateTime EndDate { get; set; }
}
Swashbuckle 为上述数据契约生成的 swagger JSON 定义变成了这样:
...
"Subscription": {
"required": ["subscriptionID", "productID", "accountID", "startDate", "endDate"],
"type": "object",
"properties": {
"subscriptionID": {
"format": "int64",
"type": "integer"
},
"productID": {
"format": "int64",
"type": "integer"
},
"accountID": {
"format": "int64",
"type": "integer"
},
"startDate": {
"format": "date-time",
"type": "string"
},
"endDate": {
"format": "date-time",
"type": "string"
}
}
},
...
但是,您会注意到 JSON definitions.Subscription.properties.startDate.format
是 "date-time"
,但 C# 代码中的 DateTypeAttribute
注释是 DataType.Date
,而不是 DataType.DateTime
.
如何在生成 swagger 文件时让 Swashbuckle 尊重 System.ComponentModel.DataAnnotations.DataTypeAttribute
?或者更具体地说,使 [DataType(DataType.Date]
注释的 class 属性生成 format
of "date"
?
format
我希望这是所有 classes 的默认行为,因为我有太多硬编码特定 class 属性的详细信息,这就是使用 Swashbuckle 生成的全部意义swagger JSON 基于同一命名空间内的其他注释(例如 System.ComponentModel.DataAnnotations.StringLengthAttribute
)。
我最初的尝试是尝试在我的 Startup.cs 中使用 ISchemaFilter,例如:
services.AddSwaggerGen(options =>
{
...
options.SchemaFilter<DataTypeSchemaFilter>();
...
});
实施过滤器 class 的位置 应用:
public class DataTypeSchemaFilter : ISchemaFilter
{
public void Apply(Schema model, SchemaFilterContext context)
{
???
}
}
但是,我发现无法使用提供的 Schema model
和 SchemaFilterContext context
参数从过滤器中调查 class 属性 属性。
如前所述,我知道 Swashbuckle 在处理 class 属性时会查看同一命名空间内的属性,因此我希望有人知道我可以在哪里绑定到 Swashbuckle 并执行类似的任务。
我写了下面的代码。从本质上讲,这个想法是将 class 上的 属性 名称连接到模式 属性 名称 (schema.properties)。鉴于您可能有自定义序列化程序设置(Camel Case),属性 名称的大小写在模式中可能与 class 中定义的不同。 我在 SetSchemaDetails 方法中也包含了父架构,以便您可以根据需要在父级添加属性。我们有偶尔使用的自定义必需属性,因此我们需要在父(包含 class)架构级别而不是 属性 架构级别指定必需属性。
public class DataAnnotationSchemaFilter : ISchemaFilter
{
public void Apply(Schema schema, SchemaFilterContext schemaFilterContext)
{
var type = schemaFilterContext.SystemType;
var propertyMappings = type
.GetProperties()
.Join(
schema.Properties ?? new Dictionary<string, Schema>(),
x => x.Name.ToLower(),
x => x.Key.ToLower(),
(x, y) => new KeyValuePair<PropertyInfo, KeyValuePair<string, Schema>>(x, y))
.ToList();
foreach (var propertyMapping in propertyMappings)
{
var propertyInfo = propertyMapping.Key;
var propertyNameToSchemaKvp = propertyMapping.Value;
foreach (var attribute in propertyInfo.GetCustomAttributes())
{
SetSchemaDetails(schema, propertyNameToSchemaKvp, propertyInfo, attribute);
}
}
}
private static void SetSchemaDetails(Schema parentSchema, KeyValuePair<string, Schema> propertyNameToSchemaKvp, PropertyInfo propertyInfo, object propertyAttribute)
{
var schema = propertyNameToSchemaKvp.Value;
if (propertyAttribute is DataTypeAttribute)
{
var dataType = ((DataTypeAttribute)propertyAttribute).DataType;
if (dataType == DataType.Date)
{
schema.Format = "date";
schema.Type = "date";
}
}
if (propertyAttribute is ReadOnlyAttribute)
{
schema.ReadOnly = ((ReadOnlyAttribute)propertyAttribute).IsReadOnly;
}
}
}