在 ASMX 中解压缩传入的 HTTP SOAP 请求

Decompress incoming HTTP SOAP requests in ASMX

我正在尝试让 IIS 接受压缩的 SOAP/XML 请求

IIS 似乎不支持开箱即用的压缩 HTTP 请求。任何带有 Content-Encoding: gzipdeflate returns HTTP 错误 400.

的 HTTP 请求

所以我尝试通过 Global.asax 添加 DelegatingHandler,如 this related question:

中所述

Global.asax:

protected void Application_Start()
{
    GlobalConfiguration.Configuration.MessageHandlers.Add(new DecompressionHandler());
}

但是在 ASMX 的情况下似乎没有执行 MessageHandlers(我检查过,Application_Start 被调用,但整个 WebApi 配置似乎在 ASMX 中没有效果。

所以问题是 - 如何在 ASMX 服务中实现 SOAP 请求解压?

能够使用 SoapExtension:

using System;
using System.Collections.ObjectModel;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Net.Http;
using System.Threading.Tasks;
using System.Web.Services.Protocols;

/// <summary>
///   ASPX SOAP request decompression WebMethod annotation
/// </summary>
[AttributeUsage(AttributeTargets.Method)]
public class CompressedRequestAttribute : SoapExtensionAttribute
{
  public override Type ExtensionType { get { return typeof(CompressedRequestExtension); } }
  public override int Priority { get { return 0; } set { } }
}

/// <summary>
///   ASPX SOAP request decompression implementation
/// </summary>
public class CompressedRequestExtension : SoapExtension
{
  public override object GetInitializer(Type serviceType) { return null; }

  public override object GetInitializer(LogicalMethodInfo methodInfo, SoapExtensionAttribute attribute) { return null; }

  public override void Initialize(object initializer) { }

  Stream origStream;
  StreamDelegate wrappedStream = new StreamDelegate();

  public override void ProcessMessage(SoapMessage message) {
    switch (message.Stage) {
      case SoapMessageStage.BeforeDeserialize:
        if (message.ContentEncoding == "gzip") {
          wrappedStream.wrapped = new GZipStream(origStream, CompressionMode.Decompress, true);
          message.ContentEncoding = null;
        }
        else if (message.ContentEncoding == "deflate") {
          wrappedStream.wrapped = new DeflateStream(origStream, CompressionMode.Decompress, true);
          message.ContentEncoding = null;
        }
        else {
          goto default;
        }
        break;
      default:
        wrappedStream.wrapped = origStream;
        break;
    }
  }

  public override Stream ChainStream(Stream stream) {
    origStream = stream;
    return wrappedStream;
  }

}

public class StreamDelegate : Stream
{
  public Stream wrapped;
  public override bool CanRead { get { return wrapped.CanRead; } }
  public override bool CanSeek { get { return wrapped.CanSeek; } }
  public override bool CanWrite { get { return wrapped.CanWrite; } }
  public override long Length { get { return wrapped.Length; } }
  public override long Position { get { return wrapped.Position; } set { wrapped.Position = value; } }
  public override void Flush() { wrapped.Flush(); }
  public override int Read(byte[] buffer, int offset, int count) { return wrapped.Read(buffer, offset, count); }
  public override long Seek(long offset, SeekOrigin origin) { return wrapped.Seek(offset, origin); }
  public override void SetLength(long value) { wrapped.SetLength(value); }
  public override void Write(byte[] buffer, int offset, int count) { wrapped.Write(buffer, offset, count); }
}

然后为每个 WebMethod 注释:

  [CompressedRequestAttribute]

以及它有效的证明:

POST /TestService/TestAction1.asmx HTTP/1.1
Host: localhost
Accept-Encoding: deflate, gzip
SOAPAction: http://example.com/TestService:action1
Content-Type: text/xml;charset=UTF-8
Content-Encoding: gzip
Content-Length: 321

<gzipped-request>

HTTP/1.1 200 OK
Content-Type: text/xml; charset=utf-8
Server: Microsoft-IIS/10.0
X-AspNet-Version: 4.0.30319
Content-Encoding: gzip
Content-Length: 10193

<gzipped-response>