在生成swagger文件时,如何使Swashbuckle尊重DataType?

时间:2017-04-17 21:39:54

标签: c# swagger swagger-2.0 swashbuckle

我正在处理以下数据合同的更复杂版本,但这应该足以作为示例:

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]注释的类属性会生成format "date"的招摇吗?

我希望这是所有类的默认行为,因为我有太多的硬编码特定类属性的细节,并且使用Swashbuckle基于同一名称空间内的其他注释生成swagger JSON的重点(例如System.ComponentModel.DataAnnotations.StringLengthAttribute)。

我最初的尝试是尝试在我的Startup.cs中使用ISchemaFilter,例如:

    services.AddSwaggerGen(options =>
    {
        ...
        options.SchemaFilter<DataTypeSchemaFilter>();
        ...
    });

过滤器类实现的位置:

public class DataTypeSchemaFilter : ISchemaFilter
{
    public void Apply(Schema model, SchemaFilterContext context)
    {
        ???
    }
}

但是,我发现无法使用提供的Schema modelSchemaFilterContext context参数调查过滤器中的类属性属性。

如前所述,我知道Swashbuckle在处理类属性时会查看同一命名空间中的属性,因此我希望有人知道我可以在哪里与Swashbuckle绑定并执行类似的任务。

1 个答案:

答案 0 :(得分:2)

我写了下面的代码。本质上,您的想法是将类的属性名称加入到架构属性名称(schema.properties)。鉴于您可能具有自定义序列化程序设置(Camel Case),属性名称的大小写在架构中可能与类中定义的不同。 我还在SetSchemaDetails方法中包含了父模式,以便您可以根据需要在父级别添加属性。我们有偶尔需要的自定义属性,因此我们需要在父级(封闭类)模式级别而不是在属性模式级别指定必需的属性。

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;
        }
    }
}