Type Inheritance Support in Swagger API¶
The Virto Сommerce platform offers enhanced support for exposing derived types in Swagger API descriptions. Traditionally, Swagger would only expose the base type, leaving derived types absent from the JSON API definition. This limitation often required additional API methods to explicitly handle derived types, complicating the development process.
Problem¶
In the example below, only the BaseObject
is exposed in Swagger. DerivedObject
and AnotherDerivedObject
will be missing from the JSON with the API definition, and the only way to expose them is to add another API method that explicitly accepts or returns one of these derived types.
On the other hand, we don't want to enable polymorphism globally, as this might break existing API (for example, any vc-module-customer
action that works with a Member
class).
// Models definition in ...Core assembly
public abstract class BaseObject
{
// ...
}
public class DerivedObject : BaseObject
{
// ...
}
public class AnotherDerivedObject : BaseObject
{
// ...
}
// Controller method that returns these models
public ActionResult<IList<BaseObject>> GetObjects()
{
var result = new[]
{
new DerivedObject(),
new AnotherDerivedObject()
};
return Ok(result);
}
Solution¶
Since the creation of VC Platform v3, polymorphism support in Swashbuckle has been significantly improved. Now we can actually use it in action, and the resulting document will be suitable for AutoRest (for example, to generate API clients for the Frontend Application). An example mentioned above can be reworked like this to expose derived models:
using Swashbuckle.AspNetCore.Annotations;
[SwaggerSubType(typeof(DerivedObject)]
[SwaggerSubType(typeof(AnotherDerivedObject)]
public abstract class BaseObject
{
// ...
}
public class DerivedObject : BaseObject
{
// ...
}
public class AnotherDerivedObject : BaseObject
{
// ...
}
This will expose BaseObject
, DerivedObject
, and AnotherDerivedObject
in the Swagger API description, even though the GetObjects()
method still only has the base type in its signature, and it won't break any other API.
Enrich Polymorphic Base Classes with Discriminator Metadata
Limitations¶
This approach works and does not break existing clients generated by AutoRest. However, it has the following limitations:
- If
BaseObject
has any abstract or virtual properties that are overridden in derived types, Swashbuckle will include those properties in bothBaseObject
and the derived types. However,AutoRest
does not understand this and throws an error like FATAL: System.InvalidOperationException: Incompatible property types found for property 'someVirtualProperty' in schema inheritance chain. One solution would be to avoid using abstract and virtual properties for such types. - This approach only works if all derived types are in the same module - it won't allow to extend this list from other modules. To work around this, we may need to write a custom subtype selector - its code would be based on the existing implementation in Swashbuckle, but would use
AbstractTypeFactory
instead of custom attributes to find descendant types. However, this may require extendingTypeInfo
so that we can explicitly specify which types can be exposed in the Swagger API.