Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Schema inheritance/polymorphism with [FromForm] parameter #2271

Open
jacobjmarks opened this issue Nov 12, 2021 · 6 comments
Open

Schema inheritance/polymorphism with [FromForm] parameter #2271

jacobjmarks opened this issue Nov 12, 2021 · 6 comments
Labels
help-wanted A change up for grabs for contributions from the community p2 Medium priority

Comments

@jacobjmarks
Copy link

I am currently attempting to generate an accurate schema that represents a polymorphic object model, however, the specification seemingly discards/ignores all inheritance/polymorphism detail.

I have created a basic example as below. Consider the following model and associated Controller Action:

// Models
public abstract class Animal
{
    public string Type { get; set; }
}

public class Cat : Animal { ... }
public class Dog : Animal { ... }
// Controller Action
[HttpPost("form")]
IActionResult Post([FromForm] Animal form) => Ok();

With certain details omitted for simplicity - such as the custom Model Binding required to properly instantiate an instance of the abstract class - I would expect an endpoint schema which is equivalent to:

/form:
  post:
    requestBody:
      content:
        multipart/form-data:
          schema:
            oneOf:
              - $ref: '#/components/schemas/Cat'
              - $ref: '#/components/schemas/Dog'

This desired schema is generated when simply using [FromBody] instead of [FromForm], though this does not meet my requirements.

However, the following is generated, lacking any sub-class detail:

# [FromForm]
/form:
  post:
    requestBody:
      content:
        multipart/form-data:
          schema:
            type: object
            properties:
              Type:
                type: string
          encoding:
            Type:
              style: form

Note that I have tried explicitly describing the inheritance via attributes but this does not help

I see the same results when attempting to utilise an interface over an abstract class.

  1. Will there be any support implemented for such a use case?
  2. How might I otherwise utilise Swashbuckle to generate my desired schema?

While this is certainly not common - from what documentation/support I have found on the matter (or lack thereof) - I am currently in the process of migrating a legacy codebase witch utilises basic HTML forms and dynamic UI elements to achieve this style of "polymorphic" form submission. Functionally, I have everything I need, I now simply required an accurate specification.

@sannyjacobsson
Copy link

Any update on this? It's annoying to be able to test some endpoints.

@peter-mederly-deltatre
Copy link

Hi, I'm also keen to hear if there's any update on the issue described here. Our use case is that we're trying to implement an OAuth 2.0 compatible identity service where the /token endpoint uses grant_type form parameter as discriminator - see spec for some scenarios here and here etc.

Using .NET 6 web API we're able to achieve it on the code level but Swagger UI is not displaying this properly because the generated open api schema is reflecting just the base model and doesn't contain any details about sub-classes (as mentioned by reporter of this issue). One of our requirements is to have all APIs properly documented so we're currently exploring some workaround using custom JS but it'd be great to know if there's a better way how to achieve desired outcome for x-www.-form-encoded request body content.

We're using the "Swashbuckle.AspNetCore" and "Swashbuckle.AspNetCore.Annotations" version 6.4.0 packages. Same thing was also reported here by another participant on this ticket.

Base model for reference:

    [SwaggerDiscriminator(KnownParameters.GrantType)]
    [SwaggerSubType(typeof(AuthorizationCodeTokenRequestModel), DiscriminatorValue = KnownGrantTypes.AuthorizationCode)]
    [SwaggerSubType(typeof(PasswordTokenRequestModel), DiscriminatorValue = KnownGrantTypes.Password)]
    [SwaggerSubType(typeof(DeviceCodeTokenRequestModel), DiscriminatorValue = KnownGrantTypes.DeviceCode)]
    public abstract class BaseTokenRequestModel
    {
        [Required]
        [BindProperty(Name = KnownParameters.GrantType)]
        [JsonPropertyName(KnownParameters.GrantType)]
        public string GrantType { get; set; }
    }

And relevant section from Startup.cs

            services.AddSwaggerGen(c =>
            {
                c.EnableAnnotations(enableAnnotationsForInheritance: true, enableAnnotationsForPolymorphism: true);
                c.UseAllOfToExtendReferenceSchemas();
                c.UseAllOfForInheritance();
                c.UseOneOfForPolymorphism();
            });

@domaindrivendev
Copy link
Owner

domaindrivendev commented Nov 15, 2022

TL;DR what you're trying to do is not currently supported and to do so will require a significant refactor to an area of the code that is already becoming a maintenance nightmare.

This is fundamentally due to the way ApiExplorer, the ASP.NET Core metadata layer that Swashbuckle is built on top of, treats [FromBody] paramers differently to [FromForm] parameters.

  • For [FromBody] it creates a single ApiParameterDescription instance that SB can inspect to get the corresponding model type (the abstract class in your example) and generate the polymorphic schema.
  • But, for [FromForm], ApiExplorer will flatten all of the class properties into multiple ApiParameterDescription instances. So, when SB is trying to generate the schema, it just has a list of property types instead of the class they're declared in, and hence doesn't have enough info to generate a polymorphic schema

Will keep open and mark as medium priority - this whole area really needs a major overahall at this point 😢

@domaindrivendev domaindrivendev added the p2 Medium priority label Nov 15, 2022
@peter-mederly-deltatre
Copy link

Hi , thanks for the feedback and for keeping the ticket open, we will explore additional options in the meantime.

@FrantisekSidak
Copy link

Hi, I ended up here in the same situation. Polymorphism with multipart form data. Is there any way to customize/rewrite part of definition?

Copy link
Contributor

github-actions bot commented May 4, 2024

This issue is stale because it has been open for 60 days with no activity. It will be automatically closed in 14 days if no further updates are made.

@github-actions github-actions bot added the stale Stale issues or pull requests label May 4, 2024
@martincostello martincostello added help-wanted A change up for grabs for contributions from the community and removed stale Stale issues or pull requests labels May 4, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
help-wanted A change up for grabs for contributions from the community p2 Medium priority
Projects
None yet
Development

No branches or pull requests

6 participants