Swagger перечисляет параметр IFormFile как тип "объект" - PullRequest
0 голосов
/ 30 октября 2018

У меня есть контроллер, который запрашивает модель, содержащую IFormFile в качестве одного из его свойств. Для описания запроса пользовательский интерфейс Swagger (я использую Swashbuckle и OpenApi 3.0 для .NET Core) перечисляет тип свойства файла как тип объекта. Есть ли способ сделать так, чтобы интерфейс Swagger обозначал точный тип, и это JSON-представление, чтобы помочь клиенту?

Контроллер, запрашивающий модель, выглядит следующим образом.

[HttpPost]
[Consumes("multipart/form-data")
public async Task<IActionResult> CreateSomethingAndUploadFile ([FromForm]RequestModel model)
{
    // do something
}

И модель определяется следующим образом:

public class AssetCreationModel
{
    [Required}
    public string Filename { get; set; }

    [Required]
    public IFormFile File { get; set; }       
}

Ответы [ 2 ]

0 голосов
/ 28 ноября 2018

Мы изучали этот вопрос сегодня. Если вы добавите следующее в ваш запуск, он преобразует IFormFile в правильный тип

services.AddSwaggerGen(c => {
   c.SchemaRegistryOptions.CustomTypeMappings.Add(typeof(IFormFile), () => new Schema() { Type = "file", Format = "binary"});
});

Также см. Следующую статью о загрузке файлов в ядре .net. https://docs.microsoft.com/en-us/aspnet/core/mvc/models/file-uploads?view=aspnetcore-2.1

0 голосов
/ 30 октября 2018

Эта проблема уже была решена в следующем выпуске / потоке github .

Это улучшение уже было объединено с Swashbuckle.AspNetCore master (по состоянию на 30.10.08), но я не ожидаю, что оно будет доступно в виде пакета в ближайшее время.

Существуют простые решения, если в качестве параметра у вас есть только IFormFile.

public async Task UploadFile(IFormFile filePayload){}

Для простого случая вы можете взглянуть на следующий ответ .

Для сложных случаев, таких как контейнеры, вы можете взглянуть на следующий ответ .

internal class FormFileOperationFilter : IOperationFilter
{
    private struct ContainerParameterData
    {
        public readonly ParameterDescriptor Parameter;
        public readonly PropertyInfo Property;

        public string FullName => $"{Parameter.Name}.{Property.Name}";
        public string Name => Property.Name;

        public ContainerParameterData(ParameterDescriptor parameter, PropertyInfo property)
        {
            Parameter = parameter;
            Property = property;
        }
    }

    private static readonly ImmutableArray<string> iFormFilePropertyNames =
        typeof(IFormFile).GetTypeInfo().DeclaredProperties.Select(p => p.Name).ToImmutableArray();

    public void Apply(Operation operation, OperationFilterContext context)
    {
        var parameters = operation.Parameters;
        if (parameters == null)
            return;

        var @params = context.ApiDescription.ActionDescriptor.Parameters;
        if (parameters.Count == @params.Count)
            return;

        var formFileParams =
            (from parameter in @params
                where parameter.ParameterType.IsAssignableFrom(typeof(IFormFile))
                select parameter).ToArray();

        var iFormFileType = typeof(IFormFile).GetTypeInfo();
        var containerParams =
            @params.Select(p => new KeyValuePair<ParameterDescriptor, PropertyInfo[]>(
                p, p.ParameterType.GetProperties()))
            .Where(pp => pp.Value.Any(p => iFormFileType.IsAssignableFrom(p.PropertyType)))
            .SelectMany(p => p.Value.Select(pp => new ContainerParameterData(p.Key, pp)))
            .ToImmutableArray();

        if (!(formFileParams.Any() || containerParams.Any()))
            return;

        var consumes = operation.Consumes;
        consumes.Clear();
        consumes.Add("application/form-data");

        if (!containerParams.Any())
        {
            var nonIFormFileProperties =
                parameters.Where(p =>
                    !(iFormFilePropertyNames.Contains(p.Name)
                    && string.Compare(p.In, "formData", StringComparison.OrdinalIgnoreCase) == 0))
                    .ToImmutableArray();

            parameters.Clear();
            foreach (var parameter in nonIFormFileProperties) parameters.Add(parameter);

            foreach (var parameter in formFileParams)
            {
                parameters.Add(new NonBodyParameter
                {
                    Name = parameter.Name,
                    //Required = , // TODO: find a way to determine
                    Type = "file"
                });
            }
        }
        else
        {
            var paramsToRemove = new List<IParameter>();
            foreach (var parameter in containerParams)
            {
                var parameterFilter = parameter.Property.Name + ".";
                paramsToRemove.AddRange(from p in parameters
                                        where p.Name.StartsWith(parameterFilter)
                                        select p);
            }
            paramsToRemove.ForEach(x => parameters.Remove(x));

            foreach (var parameter in containerParams)
            {
                if (iFormFileType.IsAssignableFrom(parameter.Property.PropertyType))
                {
                    var originalParameter = parameters.FirstOrDefault(param => param.Name == parameter.Name);
                    parameters.Remove(originalParameter);

                    parameters.Add(new NonBodyParameter
                    {
                        Name = parameter.Name,
                        Required = originalParameter.Required,
                        Type = "file",
                        In = "formData"
                    });
                }
            }
        }
    }
}

Вам нужно посмотреть, как вы можете добавить / 1020 *, который подходит для вашего случая.

...