【问题标题】:Use body stream parameter in WebApi controller's action在 Web Api 控制器操作中使用正文流参数
【发布时间】:2017-03-29 07:21:02
【问题描述】:

我目前像这样从正文中读取输入流:

public async Task<IActionResult> Post()
{
    byte[] array = new byte[Request.ContentLength.Value];

    using (MemoryStream memoryStream = new MemoryStream(array))
    {
        await Request.Body.CopyToAsync(memoryStream);
    }

    return Ok();
}

由于测试和大摇大摆的生成,我想在方法签名中指定输入参数。

是否可以以某种方式将输入参数指定为流?

public async Task<IActionResult> Post([FromBody]Stream body) ...

【问题讨论】:

标签: c# asp.net asp.net-web-api asp.net-core


【解决方案1】:

您必须创建自定义属性和自定义参数绑定。

这是我对FromContent 属性和ContentParameterBinding 绑定的实现:

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

    }

    public override Task ExecuteBindingAsync(ModelMetadataProvider metadataProvider,
                                                HttpActionContext actionContext,
                                                CancellationToken cancellationToken)
    {

    }

    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);
    }
}

现在可以

    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 没有帮助:(

【讨论】:

  • .Net Core 的任何解决方案?
【解决方案2】:

您必须为流创建模型绑定器:

public class StreamBinder : IModelBinder
{
    public Task BindModelAsync(ModelBindingContext bindingContext)
    {
        bindingContext.Result = ModelBindingResult.Success(bindingContext.HttpContext.Request.Body);
        return Task.CompletedTask;
    }
}

那么你应该为它创建模型绑定器提供者:

public class StreamBinderProvider : IModelBinderProvider
{
    public IModelBinder GetBinder(ModelBinderProviderContext context)
    {
        if (context == null)
        {
            throw new ArgumentNullException(nameof(context));
        }

        if (context.Metadata.ModelType == typeof(Stream))
        {
            return new BinderTypeModelBinder(typeof(StreamBinder));
        }

        return null;
    }
}

并注册:

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllers(options =>
    {
        options.ModelBinderProviders.Insert(0, new StreamBinderProvider());
    });
}

用法:

public async Task<IActionResult> Post([FromBody]Stream body) ...

【讨论】:

    【解决方案3】:

    AS Web Api 不在模型绑定中绑定流,这将不起作用。相反,如果您需要参数流,您可以使用自定义模型绑定

    public class MyModelBinder : IModelBinder
    {
        public bool BindModel(HttpActionContext actionContext, ModelBindingContext bindingContext)
        {
            // Extract the posted data from the request
            // Convert the base64string to Stream
            // Extract the model from the bindingcontext
            // Assign the model's property with their values accordingly   
        }
    }
    

    控制器

    public ApiActionResult SaveDocument([ModelBinder(typeof(MyModelBinder))]DocumentVM contentDTO)
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-05-12
      • 1970-01-01
      • 1970-01-01
      • 2019-02-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多