如何使用 Swashbuckle 在 Swagger API 文档/OpenAPI 规范中包含子类? [英] How do I include subclasses in Swagger API documentation/ OpenAPI specification using Swashbuckle?

查看:48
本文介绍了如何使用 Swashbuckle 在 Swagger API 文档/OpenAPI 规范中包含子类?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在 C# 中有一个 Asp.Net Web API 5.2 项目,并使用 Swashbuckle 生成文档.

我有包含继承的模型,例如从 Animal 抽象类和派生自它的 Dog 和 Cat 类的 Animal 属性.

Swashbuckle 只显示 Animal 类的模式,所以我尝试使用 ISchemaFilter(他们也建议这样做),但我无法使其工作,也找不到合适的示例.

有人可以帮忙吗?

解决方案

Swashbuckle 似乎没有正确实现多态,我理解作者关于子类作为参数的观点(如果一个动作需要一个 Animal 类并且表现如果你用狗对象或猫对象调用它,那么你应该有 2 个不同的动作......)但作为返回类型,我相信返回 Animal 是正确的,并且对象可以是 Dog 或 Cat 类型.

所以为了描述我的 API 并根据正确的指导方针生成一个合适的 JSON 模式(注意我描述鉴别器的方式,如果你有自己的鉴别器,你可能需要特别改变那部分),我使用文档和架构过滤器如下:

SwaggerDocsConfig 配置;.....configuration.DocumentFilter>();configuration.SchemaFilter>();.....公共类 PolymorphismSchemaFilter;: ISchemaFilter{private readonly Lazy>derivedTypes = new Lazy>(Init);私有静态 HashSet在里面(){var abstractType = typeof(T);var dTypes = abstractType.Assembly.GetTypes().Where(x => abstractType != x && abstractType.IsAssignableFrom(x));var result = new HashSet();foreach(dTypes 中的 var 项目)结果.添加(项目);返回结果;}public void Apply(Schema schema, SchemaRegistry schemaRegistry, Type type){if (!derivedTypes.Value.Contains(type)) 返回;var clonedSchema = 新架构{属性 = schema.properties,类型 = schema.type,required = schema.required};//schemaRegistry.Definitions[typeof(T).Name];在 SwashBuckle 中无法正常工作var parentSchema = new Schema { @ref = "#/definitions/" + typeof(T).Name };schema.allOf = new List{ parentSchema, clonedSchema };//重置包含在allOf中的属性,应该为null但代码不处理它schema.properties = new Dictionary();}}公共类 PolymorphismDocumentFilter: IDocumentFilter{公共无效应用(SwaggerDocument swaggerDoc,SchemaRegistry schemaRegistry,System.Web.Http.Description.IApiExplorer apiExplorer){RegisterSubClasses(schemaRegistry, typeof(T));}private static void RegisterSubClasses(SchemaRegistry schemaRegistry, Type abstractType){const string discriminatorName = "discriminator";var parentSchema = schemaRegistry.Definitions[SchemaIdProvider.GetSchemaId(abstractType)];//设置鉴别器属性(必须是必需的)parentSchema.discriminator = discriminatorName;parentSchema.required = new List{ 鉴别器名称 };if (!parentSchema.properties.ContainsKey(discriminatorName))parentSchema.properties.Add(discriminatorName, new Schema { type = "string" });//注册所有子类varderivedTypes = abstractType.Assembly.GetTypes().Where(x => abstractType != x && abstractType.IsAssignableFrom(x));foreach(派生类型中的变量项目)schemaRegistry.GetOrRegister(item);}}

前面的代码实现的内容在这里,在支持多态的模型"一节中指定.它基本上产生类似于以下内容:

<代码>{定义":{宠物": {类型":对象","鉴别器": "petType",特性": {姓名": {类型":字符串"},宠物类型":{类型":字符串"}},必需的": [姓名",宠物类型"]},猫": {"description": "一只猫的代表",所有的": [{"$ref": "#/定义/宠物"},{类型":对象",特性": {狩猎技能":{类型":字符串","description": "狩猎的测量技能",默认":懒惰",枚举":[无知",懒惰的","冒险",挑衅的"]}},必需的": [《狩猎技能》]}]},狗": {"description": "一只狗的代表",所有的": [{"$ref": "#/定义/宠物"},{类型":对象",特性": {包装尺寸": {"类型": "整数",格式":int32","description": "狗来自的包的大小",默认":0,最低":0}},必需的": [包装尺寸"]}]}}}

I have an Asp.Net web API 5.2 project in c# and generating documentation with Swashbuckle.

I have model that contain inheritance something like having an Animal property from an Animal abstract class and Dog and Cat classes that derive from it.

Swashbuckle only shows the schema for the Animal class so I tried to play with ISchemaFilter (that what they suggest too) but I couldn't make it work and also I cannot find a proper example.

Anybody can help?

解决方案

It seems Swashbuckle doesn't implement polymorphism correctly and I understand the point of view of the author about subclasses as parameters (if an action expects an Animal class and behaves differently if you call it with a dog object or a cat object, then you should have 2 different actions...) but as return types I believe that it is correct to return Animal and the objects could be Dog or Cat types.

So to describe my API and produce a proper JSON schema in line with correct guidelines (be aware of the way I describe the disciminator, if you have your own discriminator you may need to change that part in particular), I use document and schema filters as follows:

SwaggerDocsConfig configuration;
.....
configuration.DocumentFilter<PolymorphismDocumentFilter<YourBaseClass>>();
configuration.SchemaFilter<PolymorphismSchemaFilter<YourBaseClass>>();
.....

public class PolymorphismSchemaFilter<T> : ISchemaFilter
{
    private readonly Lazy<HashSet<Type>> derivedTypes = new Lazy<HashSet<Type>>(Init);

    private static HashSet<Type> Init()
    {
        var abstractType = typeof(T);
        var dTypes = abstractType.Assembly
                                 .GetTypes()
                                 .Where(x => abstractType != x && abstractType.IsAssignableFrom(x));

        var result = new HashSet<Type>();

        foreach (var item in dTypes)
            result.Add(item);

        return result;
    }

    public void Apply(Schema schema, SchemaRegistry schemaRegistry, Type type)
    {
        if (!derivedTypes.Value.Contains(type)) return;

        var clonedSchema = new Schema
                                {
                                    properties = schema.properties,
                                    type = schema.type,
                                    required = schema.required
                                };

        //schemaRegistry.Definitions[typeof(T).Name]; does not work correctly in SwashBuckle
        var parentSchema = new Schema { @ref = "#/definitions/" + typeof(T).Name };   

        schema.allOf = new List<Schema> { parentSchema, clonedSchema };

        //reset properties for they are included in allOf, should be null but code does not handle it
        schema.properties = new Dictionary<string, Schema>();
    }
}

public class PolymorphismDocumentFilter<T> : IDocumentFilter
{
    public void Apply(SwaggerDocument swaggerDoc, SchemaRegistry schemaRegistry, System.Web.Http.Description.IApiExplorer apiExplorer)
    {
        RegisterSubClasses(schemaRegistry, typeof(T));
    }

    private static void RegisterSubClasses(SchemaRegistry schemaRegistry, Type abstractType)
    {
        const string discriminatorName = "discriminator";

        var parentSchema = schemaRegistry.Definitions[SchemaIdProvider.GetSchemaId(abstractType)];

        //set up a discriminator property (it must be required)
        parentSchema.discriminator = discriminatorName;
        parentSchema.required = new List<string> { discriminatorName };

        if (!parentSchema.properties.ContainsKey(discriminatorName))
            parentSchema.properties.Add(discriminatorName, new Schema { type = "string" });

        //register all subclasses
        var derivedTypes = abstractType.Assembly
                                       .GetTypes()
                                       .Where(x => abstractType != x && abstractType.IsAssignableFrom(x));

        foreach (var item in derivedTypes)
            schemaRegistry.GetOrRegister(item);
    }
}

What the previous code implements is specified here, in the section "Models with Polymorphism Support. It basically produces something like the following:

{
  "definitions": {
    "Pet": {
      "type": "object",
      "discriminator": "petType",
      "properties": {
        "name": {
          "type": "string"
        },
        "petType": {
          "type": "string"
        }
      },
      "required": [
        "name",
        "petType"
      ]
    },
    "Cat": {
      "description": "A representation of a cat",
      "allOf": [
        {
          "$ref": "#/definitions/Pet"
        },
        {
          "type": "object",
          "properties": {
            "huntingSkill": {
              "type": "string",
              "description": "The measured skill for hunting",
              "default": "lazy",
              "enum": [
                "clueless",
                "lazy",
                "adventurous",
                "aggressive"
              ]
            }
          },
          "required": [
            "huntingSkill"
          ]
        }
      ]
    },
    "Dog": {
      "description": "A representation of a dog",
      "allOf": [
        {
          "$ref": "#/definitions/Pet"
        },
        {
          "type": "object",
          "properties": {
            "packSize": {
              "type": "integer",
              "format": "int32",
              "description": "the size of the pack the dog is from",
              "default": 0,
              "minimum": 0
            }
          },
          "required": [
            "packSize"
          ]
        }
      ]
    }
  }
}

这篇关于如何使用 Swashbuckle 在 Swagger API 文档/OpenAPI 规范中包含子类?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆