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

时间:2018-02-27 10:09:55

标签: asp.net-core swagger-2.0 swashbuckle

我在ASP.net核心使用Swashbuckle。它正在制作一个不错的网站,底部有一系列模型。

enter image description here

如何将模型添加到此列表中并且还没有出现?

我在我的一个请求中返回一个抽象类,我想列出继承该抽象类的所有变体。

提前致谢

5 个答案:

答案 0 :(得分:7)

您可以创建文档过滤器并在全局注册。

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>>();
    ...
}

对于多态类,您可以使用这些来过滤(this answer的稍微改进的版本)。

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客户端/生成器)以及向Swagger模式添加allOf属性,这些属性是在使用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>>();

答案 1 :(得分:1)

也许不是最干净的解决方案,但我通过在控制器上方设置ProducesResponseType属性实现了相同的目标:

[ProducesResponseType(typeof(object), 200)]
public class FileController : Controller
{

将对象替换为要在模型中显示的对象,并为每个对象创建一个新行。请确保您为每个使用不同的状态代码,否则它只显示最后一个。

答案 2 :(得分:1)

我遇到了同样的问题,我的模型没有大张旗鼓地出现,因为我函数的返回类型是抽象类型。我已经修改了上面的答案,以便可以将名称空间中的所有内容转储到模型列表中。

在“启动”中,我定义了以下功能:

public class GenericAPI_DocumentFilter<T> : IDocumentFilter where T : class
{

    public void Apply(SwaggerDocument swaggerDoc, DocumentFilterContext context)
    {

        foreach (var t in Assembly.GetExecutingAssembly().GetTypes())
        {
            if (t.Namespace.Contains("MyAPI") && t.IsClass)
            {

                var a = t.GetCustomAttribute(typeof(DataContractAttribute));
                if (a != null)
                {
                    context.SchemaRegistry.GetOrRegister(t);
                }

            }
        }

    }
}

在草率的初始化中,我添加了一行:

services.AddSwaggerGen(opt =>
            {
...
opt.DocumentFilter<GenericAPI_DocumentFilter<object>>();
...
}

答案 3 :(得分:0)

回想起来,我在下面(和其他页面上)找到的另一个答案更好,那就是添加这种属性:

[HttpGet("")]
[ProducesResponseType(typeof(MyResult), (int)System.Net.HttpStatusCode.OK)]
[ProducesResponseType(typeof(ErrorBase), (int)System.Net.HttpStatusCode.NotFound)]
... function definition ...

为什么:因为该函数根据不同的情况提供了不同类型的对象,并具有这些属性,所以您可以指定在哪种情况下返回哪些对象。

例如,我可以退还

return Ok(myresult)

return NotFound(myerror)

根据是否找到结果在函数中显示

答案 4 :(得分:0)

.NET Core 3+ 和 Shwashbuckle 5+ 使用此修改版的 Tseng original answer

这允许您将 [ApiExplorerSettings(GroupName = "my_group")] 属性应用于手动映射的类(模型、架构),以便仅在特定的单个 swagger 文档中使用它们。

using System.Linq;
using System.Reflection;
using Microsoft.AspNetCore.Mvc;
using Microsoft.OpenApi.Models;
using Swashbuckle.AspNetCore.SwaggerGen;

/// <summary>
/// 
/// </summary>
/// <typeparam name="T"></typeparam>
public class SwaggerDocumentFilter<T> : IDocumentFilter where T : class
{
    /// <summary>
    /// 
    /// </summary>
    /// <param name="openapiDoc"></param>
    /// <param name="context"></param>
    public void Apply(OpenApiDocument openapiDoc, DocumentFilterContext context)
    {
        var DocumentNames = typeof(T).GetCustomAttribute<ApiExplorerSettingsAttribute>();
        if (DocumentNames == null || !DocumentNames.GroupName.Any() || context.DocumentName == DocumentNames.GroupName)
        {
            context.SchemaGenerator.GenerateSchema(typeof(T), context.SchemaRepository);
        }
    }
}