【问题标题】:asp.net webservice handling gzip compressed requestasp.net webservice处理gzip压缩请求
【发布时间】:2011-05-21 14:20:48
【问题描述】:

我编写了一个 asp.net .asmx 网络服务来处理来自第三方工具的请求。第三方工具向 Web 服务发出 http POST 请求以获取用户信息。我正在使用 IIS7

在选中“删除所有编码”的情况下运行 Fiddler,我可以看到 Web 服务调用,并且一切正常。如果我取消选中“删除所有编码”,Web 服务调用将失败并显示 400 错误请求。我看到的不同之处在于 Fiddler 正在删除标题“Content-Encoding: gzip”并且正在解压缩内容。

所以,当 Content-Encoding 标头被移除并且内容被解压后,我的 web 服务就可以完美运行了。当 header 存在并且内容被压缩时,webservice 会失败。

我该怎么做:

  1. 配置我的 web 服务以告诉客户端它不会接受压缩请求(并希望第三方工具尊重这一点)
  2. 在 asp.net 处理的早期解压内容
  3. 修改我的网络服务以使用压缩数据

更新:需要明确的是,我不需要在响应中配置 gzip 编码,我需要处理对我的 web 服务的 gzip 编码的请求。

更新 2:第三方工具是 Salesforce.com Outlook 插件。所以,我无权修改它,它被许多其他公司使用而没有麻烦。这一定是我正在做(或不做)的事情

更新 3:我发现一篇帖子 here 说 IIS 不支持带有压缩数据的传入 POST 请求,它只支持压缩响应。这仍然是真的吗?

【问题讨论】:

    标签: asp.net web-services iis-7 asmx gzip


    【解决方案1】:

    最简单的技术是创建一个HttpModule 来替换请求过滤器。它更具可重用性,并且避免了 Global.asax。也无需创建新的解压缩流类,因为 GZipStream 已为此做好准备。这是完整的代码,它还删除了不再需要的Content-Encoding: gzip:

    public class GZipRequestDecompressingModule : IHttpModule
    {
        public void Init(HttpApplication context)
        {
            context.BeginRequest += (sender, e) =>
            {
                var request = (sender as HttpApplication).Request;
    
                string contentEncoding = request.Headers["Content-Encoding"];
    
                if (string.Equals(contentEncoding, "gzip",
                    StringComparison.OrdinalIgnoreCase))
                {
                    request.Filter = new GZipStream(request.Filter,
                        CompressionMode.Decompress);
                    request.Headers.Remove("Content-Encoding");
                }
            };
        }
        public void Dispose()
        {
        }
    }
    

    要激活此模块,请将以下部分添加到您的 web.config 中:

    <system.webServer>
        <modules runAllManagedModulesForAllRequests="true">
            <add name="AnyUniqueName"
                type="YourNamespace.GZipRequestDecompressingModule, YourAssembly"
                preCondition="integratedMode" />
        </modules>
    </system.webServer>
    

    【讨论】:

    • 是否可以只在某些路径上激活该模块,以避免任何潜在的副作用?即只为 ASMX 文件启用它?
    • 当然,在 web.config 中使用 location element,您可以根据需要在其中指定 path 属性。
    • 顺便说一句:你可以用 contentEncoding.ToLower() == "gzip" 代替 string.Equals(contentEncoding, "gzip", StringComparison.OrdinalIgnoreCase) ,这样输入起来要快得多:)
    【解决方案2】:

    由于第 3 方服务只是向您发送 POST,我认为不可能告诉他们不要以压缩格式发送。

    你可以尝试覆盖 GetWebRequest 并在途中解压

    public partial class MyWebService : System.Web.Services.Protocols.SoapHttpClientProtocol
    
    {
    protected override WebRequest GetWebRequest(Uri uri)
    {
        base.GetWebRequest(uri);request.AutomaticDecompression = System.Net.DecompressionMethods.GZip;
        return request;
    }
    }
    

    【讨论】:

    • 谢谢。不幸的是,这个请求从来没有做到这一点。您的回答使我弄清楚链中最后一个成功的调用是 PreRequestHandlerExecute ,这导致我尝试按照我的回答中所述过滤输入。
    【解决方案3】:

    GZIP 压缩是服务器的一项功能。

    如果您使用的是 IIS6,请咨询this link。

    如果您使用的是 IIS7,您可以使用 ISAPI_Rewrite 来禁用 gzip。见this link。

    也就是说,因为 gzip 是 IIS 的一个功能,所以你真的不需要做任何“特殊”的事情来让它与 Web 服务一起工作(IIS 应该处理解压缩和压缩请求)。希望这些信息能帮助您进一步排除故障并解决问题。

    【讨论】:

    • 谢谢,但到目前为止这没有帮助。我可以通过取消选中配置设置中的框来关闭 IIS7 中的压缩,但同样,这似乎只会影响来自服务器的响应。我需要处理对正在压缩的服务器的请求。
    • 这里是 gzip 客户端/服务器对话正常工作:websiteoptimization.com/speed/tweak/compress
    • 可能是你的第三方工具玩的不好。
    • 更新了帖子,第三方工具是salesforce.com的outlook插件,所以我认为它必须是我,因为其他公司都在使用它。
    【解决方案4】:

    我不确定 IIS 是否支持解压缩传入的请求,因此这可能需要进一步进行。

    Shiraz 的答案有可能奏效,这将是我会尝试的第一件事。

    如果这不起作用,您可以考虑将服务器 .asmx 服务切换到 WCF,虽然设置起来有点困难,但它也提供了更大的灵活性。

    在 WCF 方面,我可以提出两点建议。第一个很容易实现,它基于将 WCF 使用的 WebRequest 对象设置为自动接受压缩。您可以找到详细信息here。这个是 WCF 等效于 Shiraz 提出的解决方案。

    第二种更复杂,因为它涉及创建自定义消息编码器,但如果上述方法都不起作用,这应该可以解决问题。 here 描述了创建消息压缩编码器。您可能还想查看here 中的答案,它提供了消息编码器的示例配置。

    如果这有帮助或者您需要更多帮助,请告诉我。

    【讨论】:

      【解决方案5】:

      我找到了部分答案here。

      class DecompressStream : Stream
      {
          ...
      
          public override int Read(byte[] buffer, int offset, int count)
          {
              GZipStream test = new GZipStream(_sink, CompressionMode.Decompress);
      
              int c = test.Read(buffer, offset, count);
      
              return c;
          }
      
          ...
      }
      

      然后我可以像这样在请求对象上指定过滤器:

      void Application_BeginRequest(object sender, EventArgs e)
          {
              string contentEncoding = Request.Headers["Content-Encoding"];
              Stream prevCompressedStream = Request.Filter;
      
              if(contentEncoding == null || contentEncoding.Length == 0)
                  return;
      
              contentEncoding = contentEncoding.ToLower();
      
              if(contentEncoding.Contains("gzip"))
              {
                  Request.Filter = new DecompressStream(Request.Filter);
              }
          }
      

      我说部分回答是因为即使我现在可以处理传入的请求,即使响应未编码,响应也会获得“Content-Encoding: gzip”标头。我可以在 Fiddler 中验证内容未编码。

      如果我对响应进行编码,则 Web 服务的客户端会失败。似乎即使它正在发送“Accept-Encoding: gzip”,它实际上并不接受 gzip 压缩响应。我可以在 Fiddler 中验证响应是否被压缩,Fiddler 会成功解压。

      所以,现在我一直在尝试从响应中删除一个杂散的“Content-Encoding: gzip”标头。我已经从应用程序、web.config 和 IIS 中删除了所有我能找到的压缩参考。

      【讨论】:

      • 我已经好几年没有使用 ASP Web Services (.asmx) Web 服务了,但是您不能使用 HttpContext.Current.Response.Headers 访问响应标头吗?跨度>
      • @Florin,你是对的。我能够在 PostRequestHandlerExecute 函数中删除有问题的标头,并从 BeginRequest 函数中的传入请求中删除 Accelpt-Encoding 标头。必须两者都做才能使其发挥作用。
      猜你喜欢
      • 2010-12-17
      • 2016-05-26
      • 2019-05-07
      • 1970-01-01
      • 1970-01-01
      • 2011-06-02
      • 1970-01-01
      • 2011-08-21
      • 2012-08-14
      相关资源
      最近更新 更多