【问题标题】:Can I change the content of an incoming HTTP request using a HTTP Module?我可以使用 HTTP 模块更改传入 HTTP 请求的内容吗?
【发布时间】:2013-12-13 12:56:33
【问题描述】:

我正在尝试扩展我的 REST 服务(使用 WCF/webHttpBinding 构建),以便客户端可以上传压缩数据。我不确定实现这一点的最佳方法是什么,但我认为如果传入请求的 Content-Encoding 设置为 gzip,添加一个 HTTP 模块将解压缩数据会相当容易。

所以我创建了一个从 IHttpModule 派生的类,实现如下:

  private void OnBeginRequest(object sender, EventArgs e)
  {
     var app = (HttpApplication) sender;
     var context = app.Context;

     var contentEncoding = context.Request.Headers["Content-Encoding"];

     if (contentEncoding == "gzip")
     {
        // some debug code:
        var decompressedStream = new GZipStream(context.Request.InputStream, CompressionMode.Decompress);
        var memoryStream = new MemoryStream();
        decompressedStream.CopyTo(memoryStream);
        memoryStream.Seek(0, SeekOrigin.Begin);

        var streamReader = new StreamReader(memoryStream);
        string msg = streamReader.ReadToEnd();

        context.Request.InputStream.Seek(0, SeekOrigin.Begin);

        app.Request.Filter = //new TestFilterStream(app.Request.Filter);
                    new System.IO.Compression.GZipStream(
                    app.Request.Filter, CompressionMode.Decompress);
     }

  }

我看到的问题是 GZipStream 解压缩实际上从未执行过。我已经确认传入的数据实际上是 gzip 的(msg 变量包含正确的数据)。我还尝试在上面创建自己的流类 (TestFilterStream) 并将其分配给 app.Request.Filter,并且我已经确认 ASP.NET 实际上没有调用流类中的任何成员。所以看起来虽然可以指定一个过滤器,但实际上并没有使用该过滤器。

实际使用的不是HttpApplication.Request.Filter吗?

【问题讨论】:

  • 您是否尝试过在没有其他调试代码的情况下设置Request.Filter?可能是您已经读取了请求流,因此之后它将不再应用过滤器。
  • 我相信没有理由相信 Request.InputStream 是可搜索的。可能不是。尝试删除您的调试代码,无论如何它可能是错误的(您可能必须在 decompressedStream 真正将任何内容写入内存流之前关闭/刷新它)。
  • 请缩小问题范围,因为从您的问题中不清楚实际错误发生的位置和时间。问题实际上是GZipStream 没有读取底层流吗? contentEncoding 是否等于 "gzip"if 块中的代码是否被调用?您是否尝试过在调用此方法时记录?你确定你的模块被加载了吗?
  • 为什么不使用 WCF 内置的功能?可以实现IDispatchMessageInspector,解压BeforeSendReply函数中的内容。我认为您还需要将WebBodyFormatMessageProperty 设置为Raw

标签: c# asp.net wcf filter gzip


【解决方案1】:

我尝试通过两种方式设置请求过滤器:

  1. 使用 HttpModule
  2. 将其设置在 Application_BeginRequest() (Global.asax) 的开头

两者结果相同(VS2012 web project + IISExpress):

  • 如果没有输入数据(GET 请求或类似请求),则不会调用 Filter.Read
  • 如果 POST 包含实际数据,则会执行过滤器并且 Web 服务会获取过滤后的数据
  • 即使我在设置过滤器之前从 Request.InputStream 中读取,我仍然会从我的服务代码中触发过滤器。

我没有简单的方法来测试 Gzippet 输入,所以我没有尝试过实际的过滤器是否有效。但是,我知道它会被触发,因为我在尝试查找输入时收到来自 GZipStream 的错误。

也许您有其他 HttpModule 或过滤器会破坏您的输入或控制流?

This post 提出了一种与您类似的方法,但也说明了以下内容,这可能会导致一些副作用(我的测试没有使用 WCF):

“似乎这种方法在 WCF 中引发了问题,因为 WCF 依赖于原始 Content-Length,而不是解压缩后获得的值。”

【讨论】:

    【解决方案2】:

    我刚刚做了几个测试,只要有一个请求主体并且请求主体被 http 处理程序读取,我的 Request.Filter 流就会被调用。我猜你使用的是 PUT 或 POST,并且肯定会读取请求正文,所以这应该不是问题。

    我怀疑 Knaģis 的评论是正确的。您是否尝试过不使用调试代码?如果我深入研究 HttpRequest 源,我会看到一个变量 _rawContent 被写入一次;同时应用请求过滤器。之后 _rawContent 值只会被缓存,并且永远不会更新(添加过滤器时也不会重置)。

    因此,通过在调试代码中调用 Request.InputStream,您肯定会阻止稍后应用您的过滤器。读取Request.Headers集合没问题。

    【讨论】:

      【解决方案3】:

      您确定该应用程序本身应该麻烦吗? 通常这是根据主机 (IIS) 的配置来处理的。因此,基本上,您只需要在自己托管服务时实现自定义 GZip 支持。 你可以看看here

      【讨论】:

        猜你喜欢
        • 2017-10-05
        • 2018-08-14
        • 2014-08-07
        • 2011-09-28
        • 1970-01-01
        • 2010-11-29
        • 1970-01-01
        • 2019-12-07
        • 2020-12-27
        相关资源
        最近更新 更多