Hello,
I am experiencing this problem when using Swashbuckle.AspNetCore 5.0.0-rc4. This is only an issue when using the SerializeAsV2 option. All works as expected for OpenApi3.
You can find a reproduction of the issue in this repository: https://github.com/paulsavides/swagger-query-array-repro
Schema Generation
For a controller with query parameters such as:
public void Get([FromQuery]IEnumerable<int> filterId)
{
...
}
The query parameter schema is generated as:
{
"in": "query",
"name": "filterId",
"type": "array",
"items": {
"format": "int32",
"type": "integer"
}
}
In swashbuckle 4.X packages it would have been generated as:
{
"name": "filterId",
"in": "query",
"required": false,
"type": "array",
"items": {
"type": "integer",
"format": "int32"
},
"collectionFormat": "multi",
"uniqueItems": false
}
Swagger UI Query String Generation
When adding multiple filterIds through the swagger UI, the query parameter is generated as:
?filterId=1,2
The framework is unable to correctly bind this input json to the array and will generate a model validation error.
When manually adding the collectionFormat property through a custom MapType<> (included in repro repo), the query parameter is correctly generated as:
?filterId=1&filterId=2
This input binds correctly to the controller input.
Overall
I believe the property "collectionFormat": "multi" should be included on arrays in query parameters when serializing as V2. At least, the query string is generated correctly when I manually include that property.
If you require further information or if there is some simple way to solve this problem please let me know.
Thank you for your time.
In the meantime, the following filter seems to be suiting my needs fine.
public class QueryArramParamFilter : IParameterFilter
{
public void Apply(OpenApiParameter parameter, ParameterFilterContext context)
{
if (parameter.In.HasValue && parameter.In.Value == ParameterLocation.Query)
{
if (parameter.Schema?.Type == "array")
{
parameter.Schema.Extensions.Add("collectionFormat", new OpenApiString("multi"));
}
}
}
}
The default should be changed from multi to csv, as csv is the default per the Swagger 2 spec.
https://swagger.io/docs/specification/2-0/describing-parameters/#array-and-multi-value-parameters
collectionFormat | Description | Example
-- | -- | --
csv聽(default) | Comma-separated values. | foo,bar,baz
If csv is the default, it seems as if this package / Microsoft.OpenApi.NET (whichever is responsible) is respecting that. csv is the default format.
However, (If I am recalling the issue correctly) aspnetcore was unable to process values like ?filterId=1,2.
So, if there was a fix in recent aspnetcore versions that lets that be processed. Or, some toggle to use to enable that format, this issue would likely be moot.
Upgrade my reproduction repo all the way up to net5.0 and it still does not process values like ?filterId=1,2.
Perhaps this issue should be migrated to aspnetcore.
Technically, I believe this is an upstream issue with OpenAPI.NET, the library that Swashbuckle uses for serializing an OpenAPI object model to JSON & YAML, and for converting a v3 spec to a v2 spec when the SerializeAsV2 setting is applied. Specifically, the issue is with the v3 to v2 conversion.
In v3, serialization for array parameters is specified via the parameter.explode property, and it defaults to true (e.g. ?filterId=1&filterId=2) for query parameters. This is also an accepted format for the ASP.NET Core model binder and hence if you're using v3 of the spec with Swashbuckle everything will work just fine.
However, if you're using the SerializeAsV2 setting, which I'm assuming you are, the error crops up. This is because in it's conversion of the v3 spec to the v2 spec, OpenAPI.NET _does not_ carry over that same default. That is, it should be setting the corresponding collectionFormat="multi" but it's not. It turns out the "extensions" mechanism can be adpated to make this happen as described here and this provides a suitable workaround.
But, ideally the issue would be fixed upstream - would someone be willing to create a correspondin issue there?
Most helpful comment
In the meantime, the following filter seems to be suiting my needs fine.