How do I include subclasses in Swagger API documentation/ OpenAPI specification using Swashbuckle?

前端 未结 5 750
情书的邮戳
情书的邮戳 2020-11-30 00:44

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 prop

5条回答
  •  误落风尘
    2020-11-30 01:31

    We recently upgraded to .NET Core 3.1 and Swashbuckle.AspNetCore 5.0 And the API is somewhat changed. In case somebody needs this filter here is the code with minimal changes to get similar behavior:

    public class PolymorphismDocumentFilter : IDocumentFilter
    {
        public void Apply(OpenApiDocument swaggerDoc, DocumentFilterContext context)
        {
            RegisterSubClasses(context.SchemaRepository, context.SchemaGenerator, typeof(T));
        }
    
        private static void RegisterSubClasses(SchemaRepository schemaRegistry, ISchemaGenerator schemaGenerator, Type abstractType)
        {
            const string discriminatorName = "$type";
            OpenApiSchema parentSchema = null;
    
            if (schemaRegistry.TryGetIdFor(abstractType, out string parentSchemaId))
                parentSchema = schemaRegistry.Schemas[parentSchemaId];
            else
                parentSchema = schemaRegistry.GetOrAdd(abstractType, parentSchemaId, () => new OpenApiSchema());
    
            // set up a discriminator property (it must be required)
            parentSchema.Discriminator = new OpenApiDiscriminator() { PropertyName = discriminatorName };
            parentSchema.Required = new HashSet { discriminatorName };
    
            if (parentSchema.Properties == null)
                parentSchema.Properties = new Dictionary();
    
            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 item in derivedTypes)
                schemaGenerator.GenerateSchema(item, schemaRegistry);
        }
    }
    
    public class PolymorphismSchemaFilter : ISchemaFilter
    {
        private readonly Lazy> derivedTypes = new Lazy>(Init);
    
        public void Apply(OpenApiSchema schema, SchemaFilterContext context)
        {
            if (!derivedTypes.Value.Contains(context.Type)) return;
    
            Type type = context.Type;
            var clonedSchema = new OpenApiSchema
            {
                Properties = schema.Properties,
                Type = schema.Type,
                Required = schema.Required
            };
    
            // schemaRegistry.Definitions[typeof(T).Name]; does not work correctly in Swashbuckle.AspNetCore
            var parentSchema = new OpenApiSchema
            {
                Reference = new OpenApiReference() { ExternalResource = "#/definitions/" + typeof(T).Name }
            };
    
            var assemblyName = Assembly.GetAssembly(type).GetName();
            schema.Discriminator = new OpenApiDiscriminator() { PropertyName = "$type" };
            // This is required if you use Microsoft's AutoRest client to generate the JavaScript/TypeScript models
            schema.Extensions.Add("x-ms-discriminator-value", new OpenApiObject() { ["name"] = new OpenApiString($"{type.FullName}, {assemblyName.Name}") });
            schema.AllOf = new List { parentSchema, clonedSchema };
    
            // reset properties for they are included in allOf, should be null but code does not handle it
            schema.Properties = new Dictionary();
        }
    
        private static HashSet Init()
        {
            var abstractType = typeof(T);
            var dTypes = abstractType.GetTypeInfo().Assembly
                .GetTypes()
                .Where(x => abstractType != x && abstractType.IsAssignableFrom(x));
    
            var result = new HashSet();
            foreach (var item in dTypes)
                result.Add(item);
            return result;
        }
    }
    

    I did not inspect the result fully, but it seems that it gives the same behavior.

    Also note that you need to import these namespaces:

    using Microsoft.OpenApi.Models;
    using Microsoft.OpenApi.Any;
    using System.Reflection;
    using Swashbuckle.AspNetCore.SwaggerGen;
    

提交回复
热议问题