4

WE used the following post to enable compression on our Service Stack API.

Enable gzip/deflate compression.

We have the following code in my AppHost file:

public override IServiceRunner<TRequest> CreateServiceRunner<TRequest>(ActionContext actionContext)
{
    return new ApiServiceRunner<TRequest>(this, actionContext);
}

And In my ApiServiceRunner I have the following:

public override object OnAfterExecute(IRequestContext requestContext, object response)
{
    // if it's not null and not already compressed
    if ((response != null) && !(response is CompressedResult))

    // ToOptimizedResult already picks the most optimal compression (hence the name)
    response = requestContext.ToOptimizedResult(response);

    return base.OnAfterExecute(requestContext, response);
}

The problem is that this code now runs on EVERY response and we have one endpoint that just calls a json file from the server file system. When the code runs on this json file it totally kills the app pool on the server and I see a stack overflow exception when debugging an integration test that calls this json file.

So we have had to add in the following code into our AppHost file:

public override IServiceRunner<TRequest> CreateServiceRunner<TRequest>(ActionContext actionContext)
{
    bool useCustomRunner = actionContext.RequestType.Name != "HomepageLayoutConfigRequest";

    return useCustomRunner 
        ? new ApiServiceRunner<TRequest>(this, actionContext)
        : base.CreateServiceRunner<TRequest>(actionContext);
}

As you can see we don't use our custom ApiServiceRunner when the request type name is HomepageLayoutConfigRequest. This is ugly and we want a better way of doing this.

Any ideas?

thanks RuSs

ps. here is my latest AppHost CreateServiceRunner override:

    public override IServiceRunner<TRequest> CreateServiceRunner<TRequest>(ActionContext actionContext)
    {
        var requestType = actionContext.RequestType;
        string message  = "The [EnableCompression] attribute exists: {0}";

        Debug.WriteLine(string.Format("The requestType was {0}", requestType));

        var useCustomRunner = requestType.HasAttribute<EnableCompression>();
        Debug.WriteLine(string.Format(message, requestType.HasAttribute<EnableCompression>()));

        #region for serviceType if we ever need it. Currently it doesnt work as the guys at SS say it should
        // https://stackoverflow.com/questions/19127522/service-stack-enable-compression-globally
        // Commented out at there is nothing in the EndpointHost.Metadata so getting a null exception - we only need to use the attribute on the request DTO anyway.

        // @Mythz - the following code is the code that doesnt work as per my comments
        //var serviceType = EndpointHost.Metadata.GetServiceTypeByRequest(requestType);

        // @Mythz- this (serviceType) is always null. It is available in next iteration of debugging (1 iteration behind)
        //if (serviceType != null && !useCustomRunner)
        //{
        //    Debug.WriteLine(string.Format("The serviceType was {0}", serviceType));
        //    useCustomRunner = serviceType.HasAttribute<EnableCompression>();
        //    Debug.WriteLine(string.Format(message, serviceType.HasAttribute<EnableCompression>()));
        //}
        #endregion

        return useCustomRunner
            ? new ApiServiceRunner<TRequest>(this, actionContext)
            : base.CreateServiceRunner<TRequest>(actionContext);
    }
Community
  • 1
  • 1
RuSs
  • 1,725
  • 1
  • 29
  • 47

1 Answers1

2

I think you're on the right track, tho I'd prefer to use a Custom Attribute instead, e.g to only enable compression for Service classes or Request DTO's which are marked with [EnableCompression], you can do:

var serviceType = actionContext.ServiceType;
var requestType = actionContext.RequestType;

var useCustomRunner = serviceType.HasAttribute<EnableCompressionAttribute>()
                   || requestType.HasAttribute<EnableCompressionAttribute>()

return useCustomRunner
    ? new ApiServiceRunner<TRequest>(this, actionContext)
    : base.CreateServiceRunner<TRequest>(actionContext);

I personally like the declarative intent of [EnableCompression] but you can also use something like [UseCustomRunner] if your ApiServiceRunner ends up doing more than just compression.

mythz
  • 141,670
  • 29
  • 246
  • 390
  • Hey Mythz, this all looks great. Although im getting a null ref on: var serviceType = EndpointHost.Metadata.GetServiceTypeByRequest(requestType); As EndpointHost.MetaData has nothing in any of its collections. For example ServiceTypes collection is empty. Any ideas? – RuSs Oct 02 '13 at 00:10
  • Odd, does your Request and Service Name appear in the `/operations/metadata` metadata page? – mythz Oct 02 '13 at 00:13
  • It looks like what is needs here: var serviceType = EndpointHost.Metadata.GetServiceTypeByRequest(requestType); is only available in the EndpointHost.Metadata one request to this method after the current one. Ie... something is loading in the wrong order. – RuSs Oct 02 '13 at 03:31
  • All, I am still waiting on any more help to get this working. I think @mythz response it the right way to go but im running into issues as mentioed in my comments. – RuSs Oct 02 '13 at 05:30
  • @RuSs ok yeah, it looks like the ActionContext is cached before the Metadata index is built. I've just added ServiceType to ActionContext in [this commit](https://github.com/ServiceStack/ServiceStack/commit/af60f51e52fc945fd331df16e210ce3be233e91d) which will be available in next weekends release. You can use attribute the Request DTO until then (or build the v3 branch from src). I'll update the example to use the ServiceType property. – mythz Oct 02 '13 at 05:38
  • Thanks @mythz I will wait for the weekend build as we have a work around in place for the moment. After I get the new build code I will up vote you. Legend! – RuSs Oct 03 '13 at 02:24
  • I have waiting until now (@mythz) to update all my Service stack DLLs and can see the changes you have made to the code. Only thing is, I still cant see the ServiceType I need in the MetaData until 1 iteration of CreateServiceRunner after I need it. So I can currently see 10 items in the AndpointHost.Metadata.ServiceTypes but the one I need is not in the list. If I let me debugger hit this method then the next time it hits I can see the item I need. I think it may be still not right? thanks RuSs – RuSs Oct 07 '13 at 05:31
  • Sorry for mistakes in comment @mythz, it would not let me re-edit it. – RuSs Oct 07 '13 at 05:42
  • @RuSs FYI v3.9.66 has now been published to NuGet. – mythz Oct 07 '13 at 18:42
  • Thanks @mythz I am now running version 3.9.66 I am running the same code I had before and still the ServiceType is not available until 1 iteration after it is needed. I will add my code my question on this page at the top. – RuSs Oct 08 '13 at 00:23