我需要实现一个端点,该端点可以接受带有gzip压缩请求正文(而不是非压缩响应正文)的POST消息.
通过使用IRequiresRequestStream标记请求DTO,并在服务类中使用GZipStream来解压缩原始请求主体并构造一个表示解压缩数据的字符串,我找到了一种非常轻松地处理此问题的方法.它是纯文本请求正文,因此在这种情况下,跳过自动请求DTO反序列化不会丢失任何内容.
很好,我可以停在那里,但是
我在考虑是否可以以一种通用的方式编写此代码,以便如果正确设置了Content-Encoding标头,则可以解压缩发布到ServiceStack的每个请求.我写了一个看起来很不错的PreRequestFilter,直到遇到一个问题:我找不到找到修改原始请求输入流的方法,或者无法确保反序列化和稍后在管道中的其他代码可以访问解压缩的字节而不是压缩数据:
public static void Decompress(IRequest request, IResponse response)
{
if (!request.Headers[HttpHeaders.ContentEncoding].EqualsIgnoreCase(CompressionTypes.GZip))
return;
request.UseBufferedStream = true;
using (var decompressor = new GZipStream(request.InputStream, CompressionMode.Decompress))
{
var decompressedBytes = decompressor.ReadFully();
// Fails because the MemoryStream, used when UseBufferedStream
// is true, is not expandable.
// Or, if we set request.UseBufferedStream = false first, then
// it fails because the original HttpInputStream is not writeable.
request.InputStream.SetLength(decompressedBytes.Length);
request.InputStream.Write(decompressedBytes, 0, decompressedBytes.Length);
}
}
PreRequestFilters.Add(Decompress);
有什么方法可以在PreRequestFilter或RawHttpHandler中修改请求主体?或者,对于任何内容类型/ DTO,是否存在完全不同的方法来实现通用请求正文解压缩?我现在使用的按请求的实现很好,但是找到一个通用的解决方案将很有趣.
解决方法:
您可以覆盖AppHost.OnPreExecuteServiceFilter()来更改使用请求DTO执行服务的方式,例如:
class AppHost : AppHostBase
{
public virtual object OnPreExecuteServiceFilter(IService service,
object request, IRequest httpReq, IResponse httpRes)
{
if (httpReq.Headers[HttpHeaders.ContentEncoding]
.EqualsIgnoreCase(CompressionTypes.GZip))
{
//...
return customRequest;
}
return request;
}
}
使用通用的ASP.NET HTTP模块
由于这是可以在外部HTTP协议层进行通用化的,因此一个更好的选择是使用generic a GZip HttpModule,例如:
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>