ServiceStack - 如何在 JSONP 请求中使用 gzip/deflate 压缩

7

我有一个使用ServiceStack服务的应用,该服务使用RequestContext.ToOptimizedResult()压缩响应结果,例如:

[Route("/numbers/search")]
public class FindNumbers
{
}

public object Get(FindNumbers query)
{
    var data = new List<string> { "One", "Two", "Three" };
    return RequestContext.ToOptimizedResult(data);
}

当发出如下请求时,这个功能可以完美地工作:

GET http://myhost:13487/numbers/search.json

并且按照Accept-Encoding请求头的要求进行了压缩:

Accept-Encoding: gzip,deflate,sdch

我也可以发出JSONP请求:

GET http://myhost:13487/numbers/search?callback=func

正确返回未压缩的 application/javascript 回调函数。

问题

当我在 JSONP 请求中添加 Accept-Encoding 请求头时,响应是压缩的 JSON 数据,与原始的 JSON 请求一样,并没有返回一个压缩的 application/javascript 回调函数。

我是否忽略了此行为的任何明显原因,还是这只是 ServiceStack 的一个 bug? 我的期望是收到一个压缩的 JSONP 回调函数作为响应,但我对 JSONP 还不够熟悉,可能有一个很好的回退原因。

注意,我正在逐步研究 ServiceStack 源代码,但我认为我会得到更多帮助...

提前感谢

编辑

所以,我已经追踪到以下来源:

https://github.com/ServiceStack/ServiceStack/blob/5d09d439cd1a13712411552e2b3ede5a71af2ee5/src/ServiceStack/Host/Handlers/GenericHandler.cs#L79

https://github.com/ServiceStack/ServiceStack/blob/5d09d439cd1a13712411552e2b3ede5a71af2ee5/src/ServiceStack/Host/RestHandler.cs#L107

if (doJsonp && !(response is CompressedResult))
    return httpRes.WriteToResponse(httpReq, response, (callback + "(").ToUtf8Bytes(),")".ToUtf8Bytes());

return httpRes.WriteToResponse(httpReq, response);

如果响应是压缩的结果,那么不管是否需要通过?callback=func进行JSONP,响应将只包含压缩的JSON(如上例所示),这与我之前的发现相符。因此,看起来jsonp回调包装器需要在调用栈中尽早应用。

1个回答

10

对于那些感兴趣的人,我通过编写一个压缩插件来解决了这个问题。该插件会拦截响应并在服务方法之外处理压缩,我认为这是应该完成的任务。它还解决了上面描述的JSONP问题。

在我看来,压缩是与服务方法逻辑无关的问题,将其作为响应过滤器移到服务方法之外,可以使服务对服务调用具有固有的强类型,而不是使用丑陋的public object MyServiceMethod(DtoType request) { }签名来允许任意压缩/未压缩的响应。在这里,我假设如果客户端声明了有效的Accept-Encoding标头,则响应将被压缩,我认为这是一个公平的调用。

目前,我选择不向ServiceStack提交pull请求,因为我认为这是一种重大变革,涉及到框架处理压缩的方式,并需要与所有者进行大量的讨论。这段代码仅用于演示目的,但我正在使用它,并且它工作得非常好。

代码:

public class CompressionFeature : IPlugin
{
    public void Register(IAppHost appHost)
    {
        appHost.ResponseFilters.Add((request, response, dto) =>
        {
            if (dto == null || dto is AuthResponse || dto is CompressedResult || dto is Exception) return;

            using (var serializationContext = new HttpRequestContext(request, response, dto))
            {
                if (!serializationContext.RequestAttributes.AcceptsDeflate && !serializationContext.RequestAttributes.AcceptsGzip) return;

                var serializedDto = EndpointHost.ContentTypeFilter.SerializeToString(serializationContext, dto);

                var callback = request.GetJsonpCallback();
                var isJsonpRequest = EndpointHost.Config.AllowJsonpRequests && !string.IsNullOrEmpty(callback);

                if (isJsonpRequest)
                {
                    serializedDto = (callback + "(") + serializedDto + ")";
                    serializationContext.ResponseContentType = ContentType.JavaScript;
                }

                var compressedBytes = serializedDto.Compress(serializationContext.CompressionType);
                var compressedResult = new CompressedResult(compressedBytes, serializationContext.CompressionType, serializationContext.ResponseContentType);
                response.WriteToResponse(compressedResult, serializationContext.ResponseContentType);
            }
        });
    }
}

在您的 AppHost 中注册插件:

appHost.Plugins.Add(new CompressionFeature());

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