使用路由参数在Asp.Net核心MVC操作中读取原始Request.Body

时间:2017-08-21 14:51:32

标签: asp.net-core asp.net-core-mvc

我需要处理具有路由参数

的原始请求主体和MVC核心控制器
[HttpPut]
[Route("api/foo/{fooId}")]
public async Task Put(string fooId)
{
   reader.Read(Request.Body).ToList();
   await _store.Add("tm", "test", data);
}

但似乎模型绑定器在到达控制器时已经消耗了请求流。

如果删除路由参数,我可以访问请求流(因为框架将不再处理流以查找参数)。 如何指定两个路由参数并能够访问Request Body而无需手动解析请求URI等。

我尝试用[FromRoute]装饰我的参数,但它没有效果。

  • 请注意我无法将请求主体绑定到一个对象并让框架处理绑定,因为我期望一个非常大的有效负载需要以自定义方式以块的形式处理。
  • 没有其他控制器,没有自定义中间件,过滤器,序列号等。
  • 我不需要多次处理身体,只需要处理一次
  • 将流存储在临时内存或文件流中不是一个选项,我只是想直接处理请求体。

如何让框架从Uri,QueryString等绑定参数,但将请求体留给我?

2 个答案:

答案 0 :(得分:1)

我怀疑根本原因是MVC检查请求体以尝试绑定路由参数。根据{{​​3}},这是默认情况下模型绑定对非参数的任何路由的工作方式。

但是,只有在未指定请求内容类型或表单数据(我假设为multipart或url-encoded)时,框架才会执行此操作。

将我的请求内容类型更改为除表单数据之外的任何内容(例如application / json)我可以让框架忽略正文,除非特别需要(例如使用[FromBody]路由参数)。对于我的案例,这是一个可接受的解决方案,因为我只对使用内容类型application / json接受JSON有效负载感兴趣。

@Tseng指出的documentation中的DisableFormValueModelBindingAttribute的实现似乎是一种更好的方法,所以我会考虑使用它来代替完整的

答案 1 :(得分:0)

在代码中定义此属性:

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class DisableFormValueModelBindingAttribute : Attribute, IResourceFilter
{
   public void OnResourceExecuting(ResourceExecutingContext context)
   {
       var factories = context.ValueProviderFactories;
       factories.RemoveType<FormValueProviderFactory>();
       factories.RemoveType<JQueryFormValueProviderFactory>();
    }

    public void OnResourceExecuted(ResourceExecutedContext context)
    {
    }
}    

现在用它装饰你的动作方法:

[HttpPut]
[Route("api/foo/{fooId}")]
[DisableFormValueModelBinding]
public async Task Put(string fooId)
{
  reader.Read(Request.Body).ToList();
  await _store.Add("tm", "test", data);
}

该属性的工作原理是删除将尝试读取请求主体的Value Providers,只留下从路由或查询字符串提供值的那些。

HT @Tseng用于定义此属性的链接Uploading large files with streaming