使用ServiceStack支持压缩请求体

时间:2015-01-26 21:43:01

标签: c# asp.net servicestack

我需要实现一个端点,该端点可以接受带有gzip压缩请求 body 的POST消息(不是,不是压缩响应体)。

通过使用IRequiresRequestStream标记请求DTO,并在服务类中使用GZipStream来解压缩原始请求主体并构造表示解压缩数据的字符串,我找到了一种非常容易处理的方法。它是一个纯文本请求体,因此在这种情况下,跳过自动请求DTO反序列化没有任何损失。

这很好,我可以在那里停下来,但是 我正在考虑是否可以通用方式编写它,以便发布到ServiceStack的每个请求都可以解压缩,如果它正确设置了Content-Encoding标头。我写了一个看起来很不错的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的通用请求体解压缩?我现在工作的每请求实现很好,但找到一般解决方案会很有趣。

1 个答案:

答案 0 :(得分:3)

您可以覆盖AppHost.OnPreExecuteServiceFilter()以更改用于执行服务的Request 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>