Swagger не распознает параметр контроллера WebAPI с настраиваемым атрибутом [FromContent]

Я хочу иметь настраиваемый атрибут для анализа данных в виде потока и проверки с помощью Swagger.

Итак, я создал контроллер, который читает из POST тела:

[SwaggerOperation("Create")]
[SwaggerResponse(HttpStatusCode.Created)]
public async Task<string> Post([FromContent]Stream contentStream)
{
    using (StreamReader reader = new StreamReader(contentStream, Encoding.UTF8))
    {
        var str = reader.ReadToEnd();
        Console.WriteLine(str);
    }
    return "OK";
}

Как определить поток, чтобы он отображался в пользовательском интерфейсе Swagger?

Вот моя реализация атрибута FromContent и привязки ContentParameterBinding:

public class ContentParameterBinding : HttpParameterBinding
{
    private struct AsyncVoid{}
    public ContentParameterBinding(HttpParameterDescriptor descriptor) : base(descriptor)
    {

    }

    public override Task ExecuteBindingAsync(ModelMetadataProvider metadataProvider,
                                                HttpActionContext actionContext,
                                                CancellationToken cancellationToken)
    {
        var binding = actionContext.ActionDescriptor.ActionBinding;

        if (binding.ParameterBindings.Length > 1 ||
            actionContext.Request.Method == HttpMethod.Get)
        {
            var taskSource = new TaskCompletionSource<AsyncVoid>();
            taskSource.SetResult(default(AsyncVoid));
            return taskSource.Task as Task;
        }

        var type = binding.ParameterBindings[0].Descriptor.ParameterType;

        if (type == typeof(HttpContent))
        {
            SetValue(actionContext, actionContext.Request.Content);
            var tcs = new TaskCompletionSource<object>();
            tcs.SetResult(actionContext.Request.Content);
            return tcs.Task;
        }
        if (type == typeof(Stream))
        {
            return actionContext.Request.Content
            .ReadAsStreamAsync()
            .ContinueWith((task) =>
            {
                SetValue(actionContext, task.Result);
            });
        }

        throw new InvalidOperationException("Only HttpContent and Stream are supported for [FromContent] parameters");
    }
}

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Parameter, AllowMultiple = false, Inherited = true)]
public sealed class FromContentAttribute : ParameterBindingAttribute
{
    public override HttpParameterBinding GetBinding(HttpParameterDescriptor parameter)
    {
        if (parameter == null)
            throw new ArgumentException("Invalid parameter");

        return new ContentParameterBinding(parameter);
    }
}

Обновлять

Когда я создаю Stream с помощью [FromBody], он правильно отображается в Swagger, однако Stream не инициируется и ==null

[SwaggerOperation("Create")]
[SwaggerResponse(HttpStatusCode.Created)]
public async Task<string> Post([FromBody]Stream contentStream)
{
    using (StreamReader reader = new StreamReader(contentStream, Encoding.UTF8))
    {
        var str = reader.ReadToEnd();
        Console.WriteLine(str);
    }
    return "OK";
}

Хороший пост

Итак, я хочу иметь тот же пользовательский интерфейс, но с моим пользовательским атрибутом, который позволяет мне получать Stream из контента.

С моим пользовательским атрибутом он отображается без TextArea для параметра, но его можно проверить с помощью Postman и работать правильно, а Stream доступен Плохое сообщение


person v-andrew    schedule 02.06.2017    source источник
comment
Как вы хотите это показать? В чем проблема? вы должны уточнить, какой результат вы ожидаете   -  person Roman Marusyk    schedule 06.06.2017
comment
@MegaTron Я пытаюсь добавить тест для публикации, где поток был создан из тела контента, я добавлю примеры и изображения   -  person v-andrew    schedule 06.06.2017
comment
Вся эта привязка параметров кажется большой работой, чтобы избежать выполнения var stream = await this.Request.Content.ReadAsStreamAsync() в методе контроллера.   -  person Darrel Miller    schedule 20.06.2017
comment
@DarrelMiller, мне не хватает правильного способа показать var stream = await this.Request.Content.ReadAsStreamAsync(); в пользовательском интерфейсе swagger?   -  person v-andrew    schedule 20.06.2017
comment
Я не знаю, что нужно сделать, чтобы сообщить Swashbuckle, что вы принимаете поток. Однако, когда нам нужно сделать наш код более сложным, чтобы помочь управлять инструментами, которые генерируют метаданные для автоматического создания документации, если кажется, что мы делаем что-то не так.   -  person Darrel Miller    schedule 22.06.2017
comment
@DarrelMiller Я не думаю, что атрибут [FromContent] усложняет код, поскольку после создания реализации атрибута он остается там. Для меня это было интересное упражнение и полезная функция.   -  person v-andrew    schedule 22.06.2017


Ответы (2)


Наследуйте свою привязку от FormatterParameterBinding класс:

public class ContentParameterBinding : FormatterParameterBinding
{
    public ContentParameterBinding(HttpParameterDescriptor descriptor)
            : base(descriptor, 
                   descriptor.Configuration.Formatters, 
                   descriptor.Configuration.Services.GetBodyModelValidator())
    {
    }

    //your code
}
person Andriy Tolstoy    schedule 07.06.2017
comment
Спасибо за помощь! - person v-andrew; 07.06.2017

Попробуйте реализовать интерфейс IValueProviderParameterBinding:

public class ContentParameterBinding
    : HttpParameterBinding, IValueProviderParameterBinding
{
    public IEnumerable<ValueProviderFactory> ValueProviderFactories
    {
        get
        {
            return this.Descriptor.Configuration.Services.GetValueProviderFactories();
        }
    }
}

В моем случае помогло. Кроме того, он, как правило, чище, поскольку не наследует логику FormatterParameterBinding, которая может не потребоваться.

person Olexander Ivanitskyi    schedule 06.04.2018