ServiceStack – 使用gzip / deflate压缩和JSONP请求

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

 [Route("/numbers/search")] public class FindNumbers { } public object Get(FindNumbers query) { var data = new List { "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回调(未压缩)。

问题

当我将Accept-Encoding请求标头添加到JSONP请求时,响应是根据原始JSON请求的压缩JSON数据,而不是压缩的application/javascript回调。

是否有任何明显的原因导致我对此行为缺失,或者它只是ServiceStack中的一个错误? 我的期望是在响应中接收压缩的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); 

因此,如果响应是压缩结果,那么无论JSONP的要求是?callback=func ,响应将只包含压缩的json(在上面的示例中),这与我上面的发现一致。 所以看起来需要在callstack中更早地应用jsonp回调包装器。

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

在我看来,压缩是服务方法逻辑的正交关注点,并且将其作为响应filter移出服务方法之外使服务服务调用能够以固有的强类型存在而不是丑陋的public object MyServiceMethod(DtoType request) { }用于允许任意压缩/未压缩响应的签名。 我在这里假设如果客户端声明了一个有效的Accept-Encoding标头,那么无论如何都会压缩响应,我认为这是一个公平的调用。

目前,我选择了对ServiceStack的拉取请求,因为我认为这是框架处理压缩方法的一个重大变化,需要与业主进行大量的前期讨论。 这段代码纯粹是出于演示目的,但我使用它并且效果很好。

码:

 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());