在 ASMX 中解压缩传入的 HTTP SOAP 请求
Decompress incoming HTTP SOAP requests in ASMX
我正在尝试让 IIS 接受压缩的 SOAP/XML 请求。
IIS 似乎不支持开箱即用的压缩 HTTP 请求。任何带有 Content-Encoding: gzip
或 deflate
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>
我正在尝试让 IIS 接受压缩的 SOAP/XML 请求。
IIS 似乎不支持开箱即用的压缩 HTTP 请求。任何带有 Content-Encoding: gzip
或 deflate
returns HTTP 错误 400.
所以我尝试通过 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>