【问题标题】:Cannot read raw data as actionContext.Request.Content always empty in Action attribute in Web API无法读取原始数据,因为 actionContext.Request.Content 在 Web API 的 Action 属性中始终为空
【发布时间】:2019-11-08 15:33:26
【问题描述】:

我需要通过属性读取通过 post/put 发送到我的 Web API 的内容以执行一些额外的验证,但内容值始终为空,即使我可以看到 context-size 设置了一个值,即 2067 和content-type 设置为 application/json

我尝试了不同的方法,但似乎都不起作用:

  • 读取异步
  • ReadAsByteArrayAsync
  • ReadAsStringAsync
  • 等等……

我的最后一次尝试如下:

public async override void OnActionExecuting(HttpActionContext actionContext)
{
    using (MemoryStream ms = new MemoryStream())
    {
        await actionContext.Request.Content.CopyToAsync(ms)
                           .ConfigureAwait(false);
        var str = System.Text.UTF8Encoding.
                  UTF8.GetString(ms.GetBuffer(), 0, (int)ms.Length);
    }
}

我尝试使用 CopyTo 的原因是我注意到,当我调用上述一些函数时,即使它们返回一个空字符串,我也可以在谷歌搜索后进行一次调用,我相信这是通过设计。

底线是我需要访问请求的正文/内容,以便检查发送的 json。

谢谢。

【问题讨论】:

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


    【解决方案1】:

    这里的问题是执行控制器动作的中间件读取请求体并因此在进程中消耗它,从而剥夺了任何后续中间件访问它的机会。这就是为什么您在尝试解析流时得到一个空字符串的原因,因为您实际上是在读取长度为零的流。

    动作过滤器中间件似乎在控制器中间件之后运行,这在管道的过程中已经太迟了。一种解决方案是设置一个自定义中间件,该中间件在控制器中间件之前执行,并且仅在如果当前操作端点使用给定属性注释时解析请求主体流。

    此处提供的解决方案是为 ASP.NET Core 3.0 编写的,因此您可能需要进行一些调整才能使其在您的特定项目中工作。

    Startup.cs 中:

    public void Configure(IApplicationBuilder app)
    {
       app.UseRouting();   // Routes middleware
    
    
       // Request body-reading middleware must be put after UseRouting()
    
       app.Use(async (ctx, next) =>
       {
          var endpoint = ctx.GetEndpoint();
    
          if (endpoint != null)
          {
             var attribute = endpoint.Metadata.GetMetadata<ThierryAttribute>();
    
             if (attribute != null)
             {
                ctx.Request.EnableBuffering();
    
                using (var sr = new StreamReader(ctx.Request.Body, Encoding.UTF8, false, 1024, true))
                {
                   var str = await sr.ReadToEndAsync();   // Store request body JSON string
                }
    
                ctx.Request.Body.Position = 0;
             }
          }
    
          await next();
       });
    
       app.UseEndpoints(erb => erb.MapControllers());   // Controller middleware
    }
    

    并将你的属性变成一个空的标记属性:

    [AttributeUsage(AttributeTargets.Method)]
    public class ThierryAttribute : Attribute { }
    

    【讨论】:

    • 我尝试了您的建议,但不幸的是它没有奏效。由于这是在属性类中,我必须使用属性的 actionContext,而且,为了访问 Request.Body,我不得不使用:actionContext.Request.GetOwinContext().Request.Body,但不幸的是我' m 仍然得到一个空白值。
    • 对不起,刚刚看到你的观点。由于这是在调用控制器的操作之前在属性中调用的,因此我假设我的操作是对请求的第二次调用以获取反序列化对象,因此我想我需要多次读取。
    • 我认为这可能是一个重复的问题,因为我发现:stackoverflow.com/questions/13226817/… 这是我正在寻找的,但我的数据始终是一个空字符串,即使内容大小和类型是正确的。我想知道,web.config 或代码中的任何内容都可以阻止检索这些数据吗?
    • 我的解决方案有帮助吗?
    • 抱歉耽搁了。为我的原始问题找到了一个解决方法,该问题是关于如何最好地处理 (Anti)Xss 攻击,但我会在周末检查你的建议,因为我更喜欢使用原始数据而不是序列化模型。有事我会第一时间通知你的。
    猜你喜欢
    • 2014-07-31
    • 1970-01-01
    • 2013-01-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多