【问题标题】:Swagger does not recognize WebAPI controller parameter with custom attribute [FromContent]Swagger 无法识别具有自定义属性的 Web API 控制器参数 [来自内容]
【发布时间】:2017-06-02 18:18:10
【问题描述】:

我想要一个自定义属性来将数据解析为流并可以使用 Swagger 进行测试。

所以我创建了从POST body 读取的控制器:

[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 UI 中可见?

这是我对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);
    }
}

更新

当我使用 [FromBody] 创建 Stream 时,在 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";
}

所以我想拥有相同的 UI,但使用我的自定义属性,让我从内容中获得 Stream

使用我的自定义属性,它显示没有 TextArea 的参数,但可以使用 Postman 进行测试并正常工作,并且 Stream 可用

【问题讨论】:

  • 你想怎么显示?问题出在哪里?你应该澄清你期望的输出
  • @MegaTron 我正在尝试为帖子添加测试,其中流已从内容正文创建,我将添加示例和图片
  • 为了避免在控制器方法中执行var stream = await this.Request.Content.ReadAsStreamAsync(),整个参数绑定似乎需要做很多工作。
  • @DarrelMiller,我错过了在招摇 UI 中显示 var stream = await this.Request.Content.ReadAsStreamAsync(); 的正确方式吗?
  • 我不知道需要做什么才能告诉 Swashbuckle 您正在接受流。但是,当我们必须使代码更复杂以帮助驱动生成元数据以自动生成文档的工具时,如果感觉我们做错了什么。

标签: asp.net-web-api swagger swashbuckle


【解决方案1】:

FormatterParameterBinding 类继承您的绑定:

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

    //your code
}

【讨论】:

  • 感谢您的帮助!
【解决方案2】:

尝试实现接口IValueProviderParameterBinding

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

就我而言,它有所帮助。 此外,它通常更干净,因为它不继承 FormatterParameterBinding 逻辑,这可能不是必需的。

【讨论】:

    猜你喜欢
    • 2016-05-03
    • 2010-10-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-05-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多