Reputation: 1741
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)
Upvotes: 4
Views: 5913
Reputation: 731
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)){}
}
Upvotes: 2
Reputation: 1131
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.
Upvotes: 0