Asp.net-Core

使用 Swashbuckle for Asp.net core 如何將模型添加到生成的模型列表中?

  • February 27, 2018

我正在使用帶有 ASP.net 核心的Swashbuckle 。它正在製作一個很好的網站,底部有一個模型列表。

在此處輸入圖像描述

如何將尚未出現的模型添加到此列表中?

我在我的一個請求中返回了一個抽像類,我想列出繼承該抽像類的所有變體。

提前致謝

您可以創建一個文件過濾器並在​​全域範圍內註冊它。

public class CustomModelDocumentFilter<T> : IDocumentFilter where T : class
{
   public void Apply(SwaggerDocument swaggerDoc, DocumentFilterContext context)
   {
       context.SchemaRegistry.GetOrRegister(typeof(T));
   }
}

然後在你的Startup班級註冊。

services.AddSwaggerGen(options =>
{
   ...
   options.DocumentFilter<CustomModelDocumentFilter<MyCustomModel>();
   options.DocumentFilter<CustomModelDocumentFilter<MyOtherModel>();
   ...
}

對於多態類,您可以使用這些過濾器(此答案的略微改進版本)。

public class PolymorphismDocumentFilter<T> : IDocumentFilter
{
   public void Apply(SwaggerDocument swaggerDoc, DocumentFilterContext context)
   {
       RegisterSubClasses(context.SchemaRegistry, typeof(T));
   }

   private static void RegisterSubClasses(ISchemaRegistry schemaRegistry, Type abstractType)
   {
       const string discriminatorName = "$type";

       string friendlyId = abstractType.FriendlyId();
       if (!schemaRegistry.Definitions.TryGetValue(friendlyId, out Schema parentSchema))
           parentSchema = schemaRegistry.GetOrRegister(abstractType);

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

       if (parentSchema.Properties == null)
           parentSchema.Properties = new Dictionary<string, Schema>();

       if (!parentSchema.Properties.ContainsKey(discriminatorName))
           parentSchema.Properties.Add(discriminatorName, new Schema { Type = "string", Default = abstractType.FullName });

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

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

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

   public void Apply(Schema schema, SchemaFilterContext context)
   {
       if (!derivedTypes.Value.Contains(context.SystemType)) return;

       var type = context.SystemType;
       var clonedSchema = new Schema
       {
           Properties = schema.Properties,
           Type = schema.Type,
           Required = schema.Required
       };

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

       var assemblyName = Assembly.GetAssembly(type).GetName();
       schema.Discriminator = "$type";
       // This is required if you use Microsoft's AutoRest client to generate the JavaScript/TypeScript models
       schema.Extensions.Add("x-ms-discriminator-value", $"{type.FullName}, {assemblyName.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>();
   }

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

       var result = new HashSet<Type>();

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

       return result;
   }
}

需要兩個過濾器。第一個會將您傳遞的所有類添加到模式中。它還將基類中不存在的屬性添加到派生類型的架構中。

第二個過濾器添加了一些屬性($type用於模型返回時的序列化)和擴展(用於 Microsoft 的 AutoRest 客戶端/生成器)以及將allOf屬性添加到 Swagger 架構中,這是在使用 swagger 生成時創建繼承架構所必需的- gen 或 AutoRest。

註冊類似,只是需要成對註冊(只需要註冊基類)

// The following lines add polymorphism to the swagger.json schema, so that
// code generators can create properly inheritance hierarchies.
options.DocumentFilter<PolymorphismDocumentFilter<BaseClass>();
options.SchemaFilter<PolymorphismSchemaFilter<BaseClass>();

ASP.NET Core 3 和 Swashbuckle.AspNetCore 5.0 的更新

public class CustomModelDocumentFilter<T> : IDocumentFilter where T : class
{
   public void Apply(OpenApiDocument openapiDoc, DocumentFilterContext context)
   {
       context.SchemaGenerator.GenerateSchema(typeof(T), context.SchemaRepository);
   }
}

/PolymorphismDocumentFilter更新PolymorphismSchemaFilterSwashbuckle.AspNetCore5.0

public class PolymorphismDocumentFilter<T> : IDocumentFilter
{
   public void Apply(OpenApiDocument openApiDoc, DocumentFilterContext context)
   {
       RegisterSubClasses(context, typeof(T));
   }

   private static void RegisterSubClasses(DocumentFilterContext context, Type abstractType)
   {
       const string discriminatorName = "$type";
       var schemaRepository = context.SchemaRepository.Schemas;
       var schemaGenerator = context.SchemaGenerator;

       if (!schemaRepository.TryGetValue(abstractType.Name, out OpenApiSchema parentSchema))
       {
           parentSchema = schemaGenerator.GenerateSchema(abstractType, context.SchemaRepository);
       }

       // set up a discriminator property (it must be required)
       parentSchema.Discriminator = new OpenApiDiscriminator { PropertyName = discriminatorName };
       parentSchema.Required.Add(discriminatorName);

       if (!parentSchema.Properties.ContainsKey(discriminatorName))
           parentSchema.Properties.Add(discriminatorName, new OpenApiSchema { Type = "string", Default = new OpenApiString(abstractType.FullName) });

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

       foreach (var type in derivedTypes)
           schemaGenerator.GenerateSchema(type, context.SchemaRepository);
   }
}

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

   public void Apply(OpenApiSchema schema, SchemaFilterContext context)
   {
       var type = context.ApiModel.Type;
       if (!derivedTypes.Value.Contains(type))
           return;

       var clonedSchema = new OpenApiSchema
       {
           Properties = schema.Properties,
           Type = schema.Type,
           Required = schema.Required
       };

       // schemaRegistry.Definitions[typeof(T).Name]; does not work correctly in SwashBuckle
       if(context.SchemaRepository.Schemas.TryGetValue(typeof(T).Name, out OpenApiSchema _))
       {
           schema.AllOf = new List<OpenApiSchema> {
               new OpenApiSchema { Reference = new OpenApiReference { Id = typeof(T).Name, Type = ReferenceType.Schema } },
               clonedSchema
           };
       }

       var assemblyName = Assembly.GetAssembly(type).GetName();
       schema.Discriminator = new OpenApiDiscriminator { PropertyName = "$type" };
       schema.AddExtension("x-ms-discriminator-value", new OpenApiString($"{type.FullName}, {assemblyName.Name}"));

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

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

       var result = new HashSet<Type>();

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

       return result;
   }
}

引用自:https://stackoverflow.com/questions/49006079