ASP.NET Web服务处理Gzip压缩请求

9
我有一个用于处理第三方工具请求的asp.net .asmx webservice。第三方工具通过http POST请求调用webservice以获取用户信息。我正在使用IIS7。
使用选中“Remove All Encodings”的Fiddler,我可以看到webservice的调用并且一切正常运行。如果取消选中“Remove All Encodings”,则webservice的调用将失败,并显示400 Bad Request错误。我发现的区别是Fiddler删除了头部“Content-Encoding: gzip”,并对内容进行了解压缩。
因此,当Content-Encoding头被删除并且内容被解压缩时,我的webservice可以正常工作。当头部存在且内容被压缩时,webservice会失败。
我该如何做到以下之一:
1. 配置我的webservice告诉客户端它不接受压缩请求(并希望第三方工具尊重这一点)。 2. 在asp.net处理过程中提前解压缩内容。 3. 修改我的webservice以使用压缩数据。
更新:需要明确的是,我不需要在响应中配置gzip编码,而是需要处理发送到我的webservice的gzip编码请求。 更新2:第三方工具是Salesforce.com的Outlook插件。因此,我无法修改它,并且许多其他公司都在使用它而没有问题。这一定是我做错了什么(或者没有做)。 更新3:我找到了一篇文章在这里,它说IIS不支持带压缩数据的传入POST请求,它只支持压缩响应。这仍然是真的吗?
5个回答

6

最简单的技术是创建一个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 文件启用它? - NickG
1
当然,可以在web.config中使用location元素,根据需要指定path属性。 - Mart
顺便说一句:你可以使用 contentEncoding.ToLower() == "gzip" 来代替 string.Equals(contentEncoding, "gzip", StringComparison.OrdinalIgnoreCase),这样打起来更快 :) - NickG
@Mart 我已经在我的端口实现了这个功能...我遇到的问题是当进入API端点时HttpContext.Current没有被填充...在添加了这个解压器之后,我现在有了一个HttpContext.Current,但请求内容没有被填充到FromBody中...我不认为过滤器被调用了。 - Jessie Lulham

2

由于第三方服务只是向您发送一个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,这让我尝试按照我的答案描述过滤输入。 - Geoff

1

我在这里找到了部分答案

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中验证内容未被编码。

如果我对响应进行编码,webservice的客户端将失败。似乎尽管它发送了“Accept-Encoding: gzip”,但实际上它并不接受gzip压缩响应。我可以在Fiddler中验证响应已经被压缩,并且Fiddler可以成功地解压缩它。

所以,现在我陷入了困境,试图从响应中删除一个杂项的“Content-Encoding: gzip”头。我已经从应用程序、web.config和IIS中删除了所有我能找到的压缩引用。


我已经有好几年没有使用ASP Web Services (.asmx)网络服务了,但是你不能通过HttpContext.Current.Response.Headers访问响应头吗? - Florin Dumitrescu
@Florin,您是正确的。我能够在PostRequestHandlerExecute函数中删除有问题的头信息,并在BeginRequest函数中从传入请求中删除Accelpt-Encoding头信息。必须两者都做才能使其起作用。 - Geoff

1

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

如果您正在使用IIS6,请参考此链接

如果您正在使用IIS7,则可以使用ISAPI_Rewrite禁用gzip。请参见此链接

话虽如此,由于gzip是IIS的一项功能,实际上您不需要执行任何“特殊”操作即可将其与Web服务配合使用(IIS应处理请求的解压缩和压缩)。希望这些信息能让您更深入地解决问题。


谢谢,但是到目前为止这并没有帮助。我可以通过取消配置设置中的复选框来关闭IIS7中的压缩,但是这似乎只影响服务器的响应。我需要处理被压缩的请求发送到服务器的情况。 - Geoff
以下是gzip客户端/服务器对话的正常工作方式:http://www.websiteoptimization.com/speed/tweak/compress/ - Bob Black
可能是您的第三方工具不太友好。 - Bob Black
已更新帖子,第三方工具是salesforce.com的Outlook插件,因此我认为问题可能出在我这里,因为其他公司也在使用它。 - Geoff

1

我不确定IIS是否支持解压传入的请求,所以这可能需要在管道的更深处进行处理。

Shiraz的回答有可能有效,并且这是我首先尝试的方法。

如果这个方法不起作用,你可以考虑将服务器的.asmx服务切换到WCF,虽然设置稍微复杂一些,但也提供了更多的灵活性。

关于WCF方面,我可以提出两个建议。第一个建议非常容易实现,基于设置由WCF使用的WebRequest对象,自动接受压缩。你可以在这里找到具体细节。这是WCF版本的Shiraz提出的解决方案。

第二种方法更复杂,因为它涉及到创建自定义消息编码器,但如果上述任何一种方法都没用,这个方法应该可以解决问题。创建消息压缩编码器的详细步骤在这里。您还可以查看这里中呈现的消息编码器示例配置。
如果需要更多帮助,请告诉我。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接