I'm trying to upload a file with some metadata:
Upload(IFormFile file, [FromForm]IEnumerable<MetadataValue> list)
public class MetadataValue
{
public Metadata Metadata { get; set; }
public string Value { get; set; }
}
public enum Metadata
{
Cat1,
Cat2
}
A list of MetadataValue
is displayed like an array of string inputs. I was not able to determine how should I pass the data to the controller - I'm always receiving an empty collection.
Everything is working when I'm sending a request from Postman. This is handled by dot pattern/notation.
In Postman each parameter and each property of a complex object is being sent as a separate field:
curl --location --request POST 'https://localhost:44395/api/document/upload' \
--form 'file=@/C:/FilterDraft.txt' \
--form 'list[0].metadata=Cat1' \
--form 'list[0].value=ABC' \
--form 'list[1].metadata=Cat2' \
--form 'list[1].value=DEF'
How can I achieve this in Swagger UI?
How can I configure Swagger for better UI generation?
I'm using:
To add examples you have to decorate the action method with SwaggerOperationFilter:
[SwaggerOperationFilter(typeof(OperationFilter))]
Upload(IFormFile file, [FromForm]IEnumerable<MetadataValue> list)
[...]
internal class UploadOperationFilter : IOperationFilter
{
public void Apply(OpenApiOperation operation, OperationFilterContext context)
{
if (operation.OperationId != nameof(DocumentController.Upload))
{
return;
}
if (operation.RequestBody.Content.TryGetValue("multipart/form-data", out var openApiMediaType))
{
var options = new JsonSerializerOptions { WriteIndented = true };
options.Converters.Add(new JsonStringEnumConverter());
var array = new OpenApiArray
{
new OpenApiString(JsonSerializer.Serialize(new MetadataValue {Metadata = Metadata.Cat1, Value = "ABC"}, options)),
new OpenApiString(JsonSerializer.Serialize(new MetadataValue {Metadata = Metadata.Cat2, Value = "DEF"}, options))
};
openApiMediaType.Schema.Properties["metadata"].Example = array;
}
}
}
To get values in the controller (not empty collection), you have to add custom ModelBinder:
[ModelBinder(BinderType = typeof(MetadataValueModelBinder))]
public class MetadataValue
{
public Metadata Metadata { get; set; }
public string Value { get; set; }
}
public class MetadataValueModelBinder : IModelBinder
{
public Task BindModelAsync(ModelBindingContext bindingContext)
{
if (bindingContext == null)
throw new ArgumentNullException(nameof(bindingContext));
var values = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
if (values.Length == 0)
return Task.CompletedTask;
var options = new JsonSerializerOptions();
options.Converters.Add(new JsonStringEnumConverter());
var deserialized = JsonSerializer.Deserialize(values.FirstValue, bindingContext.ModelType, options);
bindingContext.Result = ModelBindingResult.Success(deserialized);
return Task.CompletedTask;
}
}