Swashbuckle MapType<Type> doesn't work with parameters
Asked Answered
S

2

4

I've got an API endpoint that takes a ShortGuid class as a parameter, like such:

[HttpGet("api/endpoint")]
public async Task<IActionResult> GetTablesAsync(ShortGuid id){}

Generates a swagger definition of:

"parameters":[
    {
        "name":"guid",
        "in":"query",
        "required":false,
        "type":"string",
        "format":"uuid"
    },
    {
        "name":"value",
        "in":"query",
        "required":false,
        "type":"string"
    }
],

I need to treat that parameter as a string, not a ShortGuid object. I already have a JsonConverter for the type that works fine, but Swashbuckle doesn't understand it so my schema is incorrect (and this my swagger-js client doesnt work). I thought MapType<> would work however that seems to only affect response objects as the schema still treats it as a ShortGuid.

c.MapType<ShortGuid>(() => new Schema { Type = "string" });

Will I require an ISchemaFilter to do this? And if so, how do I go about writing it (tried multiple attempts but no success)

Splenitis answered 29/8, 2018 at 2:26 Comment(2)
Yes an ISchemaFilter or an IDocumentFilter will take care of it... show us what you have done.Booty
Same problem! any answer?Hueyhuff
E
2

I will expose my case and solution

In my application I work with Strongly Typed Ids, like this:

[JsonConverter(typeof(IdConverterFactory))]
public partial record MyId(Guid Guid) : Id(Guid)
{
   public MyId(string guidString) : this(new Guid(guidString)){}
}

I use this kind of type type in request classes like this:

public record MyRequest(MyId Id);

[HttpGet("...")]
public async Task<IActionResult> Get([FromQuery]MyRequest request){ 
  ...
}

In the OpenApi definition is generated a parameter "Id.Guid" (should be simply Id).

If I try to make a request with ?id=some-guid, the binding will not work.

Like @AroglDarthu mentioned, the solution is creating a TypeConverter.

The following is the TypeConverter that I've created for my case:

public class IdTypeConverter<TId>: TypeConverter
{
    public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
    {
        if(sourceType == typeof(string))
        {
            return true;
        }
        return base.CanConvertFrom(context, sourceType);
    }

    public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
    {
        return Activator.CreateInstance(typeof(TId), new[] { value });
    }
}

I have a lot of strongly typed ids, so my TypeConverter is generic (for making specific ones).

Finally, in Program.cs, I track all the Id types in the Assembly and register a specific TypeConverter for each one, using the TypeDescriptor.AddAttributes method:

 typeof(MyId)
            .Assembly
            .GetTypes()
            .Where(t => t.BaseType == typeof(Id))
            .ToList()
            .ForEach(t => TypeDescriptor.AddAttributes(
                t, 
                new TypeConverterAttribute(typeof(IdTypeConverter<>).MakeGenericType(t)))
            );

Alternativally, instead of using TypeDescriptor.AddAttributes, you can annotate the types with the converter:

[JsonConverter(typeof(IdConverterFactory))]
[TypeConverter(typeof(IdTypeConverter<MyId>))]
public partial record MyId(Guid Guid) : Id(Guid)
{
   public MyId(string guidString) : this(new Guid(guidString)){}
}
Elephant answered 4/3, 2022 at 20:10 Comment(0)
K
0

For this to work on the query string, you have to add a TypeConverter for your ShortGuid.

Here is some information on why it does not work otherwise: https://github.com/dotnet/aspnetcore/issues/4825

Also note that if you use a Nullable<ShortGuid>, you will also need to add c.MapType<ShortGuid?>(...). See https://github.com/domaindrivendev/Swashbuckle.AspNetCore/issues/1648 for that.

Kolomna answered 22/10, 2021 at 19:10 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.