Nancy: How to inject a dependency to NancyModule which itself depends on the Request?

Created on 25 Feb 2018  路  4Comments  路  Source: NancyFx/Nancy

Prerequisites

  • [x] I have written a descriptive issue title
  • [x] I have verified that I am running the latest version of Nancy
  • [x] I have verified if the problem exist in both DEBUG and RELEASE mode
  • [x] I have searched open and closed issues to ensure it has not already been reported

Description

Sometimes I need to read data from the request itself to initialize a service that a certain NancyModule depends on. Take the following example:

public interface IApiKeyProvider 
{
     string GetApiKey();
}

public class Service : IService
{
    private readonly IApiKeyProvider apiKeyProvider; 
    public Service(IApiKeyProvider apiKeyProvider) 
    {
           this.apiKeyProvider = apiKeyProvider;
    }

    // ... implementation
}

I would register both like this:

container.Register<IService, Service>();
container.Register<IApiKeyProvider, FromRequestApiKeyProvider>(); 

where the class FromRequestApiKeyProvider provides the api key by reading data from the request. This is what I need to be able to do:

// Hypothetical class
public class FromRequestApiKeyProvider : IApiKeyProvider
{
    private readonly NancyRequest request;
    public FromRequestApiKeyProvider(NancyRequest request)
    {
         this.request = request;
    }

    public string GetApiKey() 
    {
         return this.request.Query["apiKey"];
    }
}

Not being able to do this forces me into initializing a concrete implementation of Service inside the request handlers which in turn forbids me from unit-testing my NancyModule.

Another scenario where this popped up is where I put data into the Items of a NancyContext in the Before pipeline (in Bootstrapper -> ApplicationStartup) and then needing to read the data I put in the NancyContext to initialize some service like the above.

As an example for the latter scenario, see the Nancy.Serilog library where you have to get a contexual logger from inside the request handler i.e. var logger = this.CreateLogger(); where ideally this would be injected as a dependency to the NancyModule at the constructor level.

Most helpful comment

I don't know about standard, but I think it can be achieved by registering the context or request in the request container. Just override ConfigureRequestContainer in your bootstrapper.

All 4 comments

Can't you just register the context or request in the request container?

Wait, there is a standard way to do this 馃槄, please, where can I find examples?

I don't know about standard, but I think it can be achieved by registering the context or request in the request container. Just override ConfigureRequestContainer in your bootstrapper.

Wow, this actually worked, thanks a lot :heart: :pray: I used this code:

protected override void ConfigureRequestContainer(TinyIoCContainer container, NancyContext context)
{
    Func<ILogger> getLogger = () =>
    {
        if(!context.Items.ContainsKey("RequestId"))
        {
            return Log.Logger;
        }

        var requestId = (string)context.Items["RequestId"];
        var contextualLogger = Log.ForContext("RequestId", requestId);
        return contextualLogger;
    };

    container.Register((tinyIoc, namedParams) => getLogger());
}
Was this page helpful?
0 / 5 - 0 ratings

Related issues

mylemans picture mylemans  路  9Comments

cody82 picture cody82  路  9Comments

Hell0wor1d picture Hell0wor1d  路  12Comments

jchannon picture jchannon  路  9Comments

thecodejunkie picture thecodejunkie  路  4Comments