如何在IIS 7上为POST(上传)请求启用GZIP压缩以供SOAP WebService使用?

10
我该如何为上传到.NET WebService(SOAP,而非WCF)的POST数据启用压缩?我认为只需在IIS中启用动态压缩即可解决问题,但实际上启用后只有响应被压缩了,而没有压缩POST 请求
我已将其添加为服务引用,并且无法在生成的SOAPClient上找到任何设置以启用请求的压缩。
看起来我可能在客户端缺少配置设置或代码,以在将请求发送到服务器之前压缩请求?还是说我正在尝试的内容(GZipping POST数据)甚至不受支持?
更多信息:我在客户端和服务器上使用的是.NET 4.0。

请参考此SO帖子(https://dev59.com/_VLTa4cB1Zd3GeqPbpSl) - Rajesh
2个回答

14

我记得在几个地方使用了try-catch来使它更可靠,并且在生产环境下,即使在重负载下,它已经运行了4年。 - Wiktor Zychla
我已经解决了一个问题,但我不喜欢的是,如果操作超时,你不会得到“操作超时”消息,而只是“对象未设置为对象的实例”。这使得很难确定实际出了什么问题。你是否对此进行了任何修复?如果有,你能否提供一个示例? - NickG
很遗憾,我不记得任何可能超时的具体修复方法。 - Wiktor Zychla
@NickG:是的,我们遇到了不兼容代理的问题。有些问题无法解决。 - Wiktor Zychla
我正在尝试通过检查流的前几个字节来确定它是否可修复,以查看是否包含gzip魔术数字,如果是,则附加缺失的“Content-Encoding: gzip”头。但是我在自己的机器上很难复制这个问题,所以如果无法复制它,我怀疑我不能修复它 :( - NickG
显示剩余7条评论

7
最终,我采用了Wiktor Zychla的答案,但遇到了一些错误(也在他的文章评论中提到)。为了完整起见,我将发布我的HttpCompression模块的修复版本,但您需要的其余代码(和实现指南)都在Wiktor的文章中。
更新:
实际上,我不得不停止使用此代码,因为它有一个无法修复的错误(即使使用我下面改进的版本)。对于许多使用代理服务器的人来说,它会出现一个错误,显示“gzip头中的幻数不正确”,我无法解决这个问题。我认为这是因为代理服务器解压缩GZIP,而此代码在当前形式下不允许接收经过压缩和未经压缩的响应。
using System;
using System.IO.Compression;
using System.Web;
using System.Web.Security;

/// <summary>
/// Implement two way HTTP compression for the SOAP API
/// Code taken from: http://netpl.blogspot.co.uk/2009/07/aspnet-webservices-two-way-    response-and.html
/// Fix: Set Content-encoding: gzip header when an Exception occurs on the server.
/// Fix: Do not attempt to decrypt GZIP request stream when request was not GZIPed.
/// </summary>
public class HttpCompressionModule : IHttpModule
{
    private bool isDisposed = false;

    ~HttpCompressionModule()
    {
            Dispose(false);
    }

    public void Init(HttpApplication context)
    {
            context.BeginRequest += new EventHandler(Context_BeginRequest);
            context.PreSendRequestHeaders += new EventHandler(context_PreSendRequestHeaders);
    }

    void context_PreSendRequestHeaders(object sender, EventArgs e)
    {
            // Fix headers having been lost if an exception occurred.
            HttpApplication app = sender as HttpApplication;
            HttpContext ctx = app.Context;
            if (app.Response.Filter is GZipStream) SetEncoding("gzip");
            else if (app.Response.Filter is DeflateStream) SetEncoding("deflate");

            // Fix double header
            if (ctx.Response.Headers["Content-encoding"] == "gzip,gzip")
                    ctx.Response.Headers.Set("Content-encoding", "gzip");
    }

    public void Context_BeginRequest(object sender, EventArgs e)
    {
            HttpApplication app = sender as HttpApplication;
            HttpContext ctx = app.Context;

            // Currently only enable for the Uploader API webservice
            if (!ctx.Request.Url.PathAndQuery.ToLower().Contains("uploaderapi.asmx"))
            {
                    return;
            }

            // Add request filter if request was GZIP encoded
            string requestEncoding = ctx.Request.Headers["Content-encoding"];
            if (requestEncoding != null && requestEncoding == "gzip")
            {
               app.Request.Filter =
                    new System.IO.Compression.GZipStream(app.Request.Filter, CompressionMode.Decompress);
            }

            // Add response compression filter if the client accepts compressed responses
            if (IsEncodingAccepted("gzip"))
            {
                    app.Response.Filter = new GZipStream(app.Response.Filter, CompressionMode.Compress);
                    SetEncoding("gzip");
            }
            else if (IsEncodingAccepted("deflate"))
            {
                    app.Response.Filter = new DeflateStream(app.Response.Filter, CompressionMode.Compress);
                    SetEncoding("deflate");
            }
    }

    private bool IsEncodingAccepted(string encoding)
    {
            return HttpContext.Current.Request.Headers["Accept-encoding"] != null &&
                    HttpContext.Current.Request.Headers["Accept-encoding"].Contains(encoding);
    }

    private void SetEncoding(string encoding)
    {
            HttpContext ctx = HttpContext.Current;
            string responseEncodings = ctx.Response.Headers.Get("Content-encoding");
            if (responseEncodings == null || !responseEncodings.Contains(encoding))
                    HttpContext.Current.Response.AppendHeader("Content-encoding", encoding);
    }

    public void Dispose()
    {
            Dispose(true);
    }

    private void Dispose(bool dispose)
    {
            isDisposed = dispose;
    }
}

2
Nick,请记住这不是完整的答案。同时需要HttpResponseDecompressed和HttpRequestCompressed。 - Wiktor Zychla
@WiktorZychla 谢谢 - 我已经更新了我的答案,以明确你的文章包含所有的代码/说明。我的回答只是为了修复我发现的那些错误/兼容性问题(如果你决定将它们纳入你的文章中,我的回答将会变得多余)。 - NickG
Nick,由于您的清晰解释,我不打算对该条目进行任何修改。我假设任何试图使用它的人都会发现您的评论并遵循您更新的方法。很高兴这个解决方案有所帮助,感谢您的修复。 - Wiktor Zychla

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