Tolga Erdogus - 359 - Jan 26, 2015

Can I please have some help with: http://stackoverflow.com/questions/28137860/how-can-servicestack-redismqserver-registerhandler-call-a-service

Thanks

Can you break this into more specific questions.  It’s unclear what the question is and how to best answer. There are no Cookies in an MQ Request so you wont have access to the Users Session. You would need to explicitly pass the UserId or Users sessionId and retrieve it from the ICacheClient.

MQ Services are generally “internal” Services, i.e. they don’t travel through the normal Global RequestFilters:
https://github.com/ServiceStack/ServiceStack/wiki/Architecture-overview
I’d treat them as internal/trusted services. You can restrict Services using the [Restrict] attribute:
https://github.com/ServiceStack/ServiceStack/wiki/Restricting-Services

Tolga Erdogus:

Thanks for the quick reply.

My first question would be how do I ideally (servicestack best practice-wise) setup an async version of an existing http service that would run in its own threadpool?  The goal is to offload long running requests…

I think you answered my second question, which was “why is session context not being passed to the long running http service when called from the mq handler?”.  I understand your reply as needing to send the sessionId as part of the request and look it up before executing session and authId dependent logic.

Third question is why doesn’t the mq handler actually call the LongRunnigService in:
mqHost.RegisterHandler<LongRunningRequest>(m =>
            {
                return this.ServiceController.ExecuteMessage(m);
            });

but this calls the service successfully (but misses the session related stuff):
mqHost.RegisterHandler<LongRunningRequest>(m =>
            {
                this.ResolveService<LongRunningService>().Any(m.getBody());
            });

Thanks again.

Weird, the Service should run, is it throwing an exception or otherwise short-circuiting? Can you debug it and let me know what  this.ServiceController.ExecuteMessage(m); is returning?

Tolga Erdogus:

I am getting a Value cannot be null.  Parameter name: key…
It’s a 400, Bad Request…

Do you have some custom validation that’s causing this? Is there a StackTrace? You can turn on DebugMode: https://github.com/ServiceStack/ServiceStack/wiki/Debugging#debugmode

Tolga Erdogus:

Is there a place where I can get symbols for servicestack?  Following the debug steps I only am able to step in to the Microsoft stack.
I am able to circumvent this issue by using this.ResolveService<LongRunningService>().Any(m.getBody()); so that part is not that urgent.

But I guess my real roadblock question is, once my mq handler is able to call my “real” http service which relies on session and session.GetOAuthTokens, how do I go about getting the session from the userAuthId that I am going to pass in from the original async wrapper service?

I don’t see anything in ICacheClient that is something like GetSessionFromAuthId(…).

I am basically calling linkedin api functions and thus need to have access to UserAuthDetails - that’s what all the hoopla is about.

The symbols for ServiceStack should be published on http://www.symbolsource.org/Public/Wiki/Using

MQ Requests aren’t executed inside any HTTP Context, so there’s no Cookies or other HTTP Request Context Info like Headers, FormData, etc. This also means there’s no Session available which is based on the ss-id/ss-pid Cookies.

But you should be able to inject a session with:

var req = new BasicRequest();
req.Items[ServiceExtensions.RequestItemsSessionKey] = new AuthUserSession { … }; // i.e. req.Items["__session"]

Or pass the Session Cookie Ids in the HTTP Headers: 

req.Headers[“X-ss-id”] = sessionId;

And then Execute your Service with this custom Request Context, e.g:

return this.ServiceController.ExecuteMessage(m, req);

Otherwise you can just access the Users Session from the CacheClient directly, e.g:

var sessionKey = SessionFeature.GetSessionKey(sessionId); //i.e. urn:iauthsession:{sessionId}
var usersSession  = HostContext.TryResolve<ICacheClient>().Get<IAuthUserSession>(sessionKey);

More info about Sessions in ServiceStack is in:
https://github.com/ServiceStack/ServiceStack/wiki/Sessions

Tolga Erdogus:

Thank you Demis!

Tolga Erdogus:

Just want to say that this information was perfect!  Thanks again…

Tolga Erdogus:

In regards to the debugging and why the ExecuteMessage wasn’t working - it happens here:

   public void RegisterServiceExecutor(Type requestType, Type serviceType, ITypeFactory serviceFactoryFn)
        {
            ResetServiceExecCachesIfNeeded(serviceType, requestType);

            var serviceExecDef = typeof(ServiceRequestExec<,>).MakeGenericType(serviceType, requestType);
            var iserviceExec = (IServiceExec)serviceExecDef.CreateInstance();

            ServiceExecFn handlerFn = (requestContext, dto) =>
            {
                var service = serviceFactoryFn.CreateInstance(serviceType);

                ServiceExecFn serviceExec = (reqCtx, req) =>
                    iserviceExec.Execute(reqCtx, service, req);  /// HAPPENS ON THIS LINE - I CAN’T STEP IN TO THIS and it returns 

                return ManagedServiceExec(serviceExec, (IService)service, requestContext, dto);
            };

            AddToRequestExecMap(requestType, serviceType, handlerFn);
        }

This probably happens because the service is [Authenticate] protected.

Unfortunately turning my code to the following still doesn’t seem to work for the carrying over the session and auto-authenticating the service:
 mqHost.RegisterHandler<LongRunningRequest>(m =>
            {
                var req = new BasicRequest();
                req.Headers[“X-ss-id”] = m.GetBody().SessionId; //I send the SessionId from the async version of the service from the this.GetSession().Id
                var res = this.ServiceController.ExecuteMessage(m, req);
                
                return res;
            });

Shouldn’t this theoretically make [Authenticate] happy and set up all the session info inside the service?

I’ve just added a MqAuthOnly example mimicking what you’re trying to do here in this commit; https://github.com/ServiceStack/ServiceStack/commit/514a2de12d7aad9706f4074b167e3e3241a58166

The above error was due to BasicRequest not having a Verb set which you can do with: 

var req = new BasicRequest { Verb = HttpMethods.Post };

I’ve also added POST as the default Verb for BasicRequest which will be available in the next release.

Also I wanted to highlight a difference between HTTP vs MQ services mentioned earlier on about MQ being internal/trusted services which don’t execute Global Request filters, illustrated with the architecture diagram: https://github.com/ServiceStack/ServiceStack/wiki/Architecture-overview

In practice this means to only have [Authenticate] Request Filter attribute executed in HTTP Services you can add it to the Service class, e.g:

[Authenticate]
public class MqAuthOnlyService : Service 
{
    public object Any(MqAuthOnly request)
    {
        var session = base.SessionAs<AuthUserSession>();
        return new MqAuthOnlyResponse {
            Result = “Hello, {0}! Your UserName is {1}”
                .Fmt(request.Name, session.UserAuthName)
        };
    }
}

To have it executed for both MQ and HTTP Requests add it on the method (i.e Action Filter Attribute), e.g:

public class MqAuthOnlyService : Service 
{
    [Authenticate]
    public object Any(MqAuthOnly request)
    {
        var session = base.SessionAs<AuthUserSession>();
        return new MqAuthOnlyResponse {
            Result = “Hello, {0}! Your UserName is {1}”
                .Fmt(request.Name, session.UserAuthName)
        };
    }
}