dan
dan

Reputation: 1954

Swagger Swashbuckle Polymorphism doesn't work with interface types

With the code below I'm trying to mention alternative schemas; which share the same interface, as the type of the response.

Startup.cs

services.AddSwaggerGen(c =>
{
    c.UseOneOfForPolymorphism();
});

app.UseSwagger();
app.UseSwaggerUI(options =>
{
    options.SwaggerEndpoint("/swagger/v1/swagger.json", "v1");
});

Models

public interface IOutput
{
    string Discriminator { get; }
}

public class OutputA : IOutput
{
    public string Discriminator => GetType().Name;

    public string PropA { get; set; }
}

public class OutputB : IOutput
{
    public string Discriminator => GetType().Name;

    public string PropB { get; set; }
}

Controller

[HttpGet]
[ProducesResponseType(typeof(IOutput), StatusCodes.Status200OK)]
public IActionResult Get()
{
    return Ok();
}

I'm expecting both types OutputA and OutputB will be listed as return types, But only IOutput is mentioned:

enter image description here

Alternatively, If I change the IOutput from an interface to a class or an abstract class then OutputA and OutputB will be listed as valid return types:

enter image description here

Is this not supported due to a RESTFul/API standard?

Or can this be achieved?

This is a .NET Core 2.2 project, using Swashbuckle.AspNetCore v6.2.3

Upvotes: 4

Views: 6765

Answers (2)

joe_maya
joe_maya

Reputation: 195

I ran into the problem of swagger gen not including schema for types that implement an interface (works for abstract base class/record). I used the following solution as it is more generic than specifying individual types.

builder.Services.AddSwaggerGen(opts => {    
    opts.UseOneOfForPolymorphism();
    //Needed to generate schema from derived types of an interface (base class/record already supported) 
    opts.SelectSubTypesUsing(baseT => {
        return Assembly.GetAssembly(typeof(<SomeTypeInTheLibYouwant>))?.GetTypes()
                        .Where(derivedT => baseT.IsAssignableFrom(derivedT));
    });
});

Upvotes: 1

dan
dan

Reputation: 1954

Sharing the solution I found, just in case if someone may need it.

The idea is to add the polymorphic relationships explicitly through the AddSwaggerGen() via the SelectSubTypesUsing() as:

services.AddSwaggerGen(c =>
{
    c.UseOneOfForPolymorphism();
    c.SelectSubTypesUsing(baseType =>
    {
        if (baseType == typeof(IOutput))
        {
            return new[]
            {
                typeof(OutputA),
                typeof(OutputB)
            };
        }
        return Enumerable.Empty<Type>();
    });
});

Upvotes: 10

Related Questions