我可以使用HTTP模块更改传入HTTP请求的内容吗?

时间:2022-06-13 23:14:28

I'm trying to extend my REST service (built using WCF/webHttpBinding) so that clients can upload gzipped data. I'm not sure what the best way to accomplish this but I thought it would be fairly easy by adding a HTTP module which will decompress the data if Content-Encoding for the incoming request is set to gzip.

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

So I created an class deriving from IHttpModule with the following implementation:

所以我创建了一个派生自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);
     }

  }

The issue I'm seeing is that the GZipStream decompression is never actually performed. I've confirmed that the incoming data is in fact gzip'd (the msg-variable contains the proper data). I've also tried creating my own stream class (TestFilterStream) above and assign that to app.Request.Filter and I've confirmed that no members on the stream class is actually called by ASP.NET. So it seems like while it's possible to specify a filter, that filter isn't actually used.

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

Isn't HttpApplication.Request.Filter actually used?

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

3 个解决方案

#1


2  

I tried setting the Request Filter in two ways:

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

  1. Using a HttpModule
  2. 使用HttpModule

  3. Setting it in the start of Application_BeginRequest() (Global.asax)
  4. 在Application_BeginRequest()(Global.asax)的开头设置它

Both with the same results (VS2012 web project + IISExpress):

两者都有相同的结果(VS2012网络项目+ IISExpress):

  • If there is no input data (GET request or similar), the Filter.Read is not invoked
  • 如果没有输入数据(GET请求或类似),则不调用Filter.Read

  • In case of a POST with actual data, the filter is executed and the web service gets the filtered data
  • 在具有实际数据的POST的情况下,执行过滤器并且web服务获得过滤的数据

  • Even if I read from the Request.InputStream before the Filter is set, I still get the filter triggered from my service code.
  • 即使我在设置过滤器之前从Request.InputStream中读取,我仍然会从我的服务代码中触发过滤器。

I have no easy way of testing with Gzippet input, so I have not tried if the actual filter works. However, I know it is getting triggered, since I get an error from GZipStream while it attempts to look for the input.

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

Perhaps you are having other HttpModules or Filters that disrupt your input or control flow?

也许您正在使用其他HttpModule或过滤器来破坏您的输入或控制流程?

This post proposes a method similar to yours, but also states the following, which may be causing some side effects (my tests were not using WCF):

这篇文章提出了一个类似于你的方法,但也说明了以下内容,这可能会导致一些副作用(我的测试没有使用WCF):

"It appears that this approach trigger a problem in WCF, as WCF relies on the original Content-Length and not the value obtained after decompressing."

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

#2


0  

I've just done a couple of tests, and my Request.Filter stream is called into, as long as there is a request body and the request body gets read by the http handler. I'm guessing you use either a PUT or a POST, and certainly read the request body, so that shouldn't be a problem.

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

I suspect Knaģis' comment is correct. Have you tried without the debug code? If I dig into the HttpRequest source, I see a variable _rawContent being written to exactly once; at that same time the request filters are applied. After that the _rawContent value just gets cached, and is never updated (nor reset when a filter is added).

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

So by calling Request.InputStream in your debug code you are definitely preventing your filter from being applied later on. Reading the Request.Headers collection is no problem.

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

#3


0  

Are you sure, that application itself should bother? Usually that's handled per configuration of host (IIS). So, basically, you only need to implement custom GZip support, when you host the service yourself. You can take a look here

你确定,该应用程序本身应该打扰吗?通常根据主机(IIS)的配置进行处理。所以,基本上,当您自己托管服务时,您只需要实现自定义GZip支持。你可以看看这里

#1


2  

I tried setting the Request Filter in two ways:

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

  1. Using a HttpModule
  2. 使用HttpModule

  3. Setting it in the start of Application_BeginRequest() (Global.asax)
  4. 在Application_BeginRequest()(Global.asax)的开头设置它

Both with the same results (VS2012 web project + IISExpress):

两者都有相同的结果(VS2012网络项目+ IISExpress):

  • If there is no input data (GET request or similar), the Filter.Read is not invoked
  • 如果没有输入数据(GET请求或类似),则不调用Filter.Read

  • In case of a POST with actual data, the filter is executed and the web service gets the filtered data
  • 在具有实际数据的POST的情况下,执行过滤器并且web服务获得过滤的数据

  • Even if I read from the Request.InputStream before the Filter is set, I still get the filter triggered from my service code.
  • 即使我在设置过滤器之前从Request.InputStream中读取,我仍然会从我的服务代码中触发过滤器。

I have no easy way of testing with Gzippet input, so I have not tried if the actual filter works. However, I know it is getting triggered, since I get an error from GZipStream while it attempts to look for the input.

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

Perhaps you are having other HttpModules or Filters that disrupt your input or control flow?

也许您正在使用其他HttpModule或过滤器来破坏您的输入或控制流程?

This post proposes a method similar to yours, but also states the following, which may be causing some side effects (my tests were not using WCF):

这篇文章提出了一个类似于你的方法,但也说明了以下内容,这可能会导致一些副作用(我的测试没有使用WCF):

"It appears that this approach trigger a problem in WCF, as WCF relies on the original Content-Length and not the value obtained after decompressing."

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

#2


0  

I've just done a couple of tests, and my Request.Filter stream is called into, as long as there is a request body and the request body gets read by the http handler. I'm guessing you use either a PUT or a POST, and certainly read the request body, so that shouldn't be a problem.

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

I suspect Knaģis' comment is correct. Have you tried without the debug code? If I dig into the HttpRequest source, I see a variable _rawContent being written to exactly once; at that same time the request filters are applied. After that the _rawContent value just gets cached, and is never updated (nor reset when a filter is added).

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

So by calling Request.InputStream in your debug code you are definitely preventing your filter from being applied later on. Reading the Request.Headers collection is no problem.

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

#3


0  

Are you sure, that application itself should bother? Usually that's handled per configuration of host (IIS). So, basically, you only need to implement custom GZip support, when you host the service yourself. You can take a look here

你确定,该应用程序本身应该打扰吗?通常根据主机(IIS)的配置进行处理。所以,基本上,当您自己托管服务时,您只需要实现自定义GZip支持。你可以看看这里