2

I have a self hosted Owin application that uses Nancy. In one of the NancyModules I need to get an instance of IOwinContext.

This question touches on the subject, but there's no solution in it: Get current owin context in self host mode

It says that for Nancy, you have to use NancyContext to get to the Items dictionary and look for the value corresponding to the key "OWIN_REQUEST_ENVIRONMENT".

I do have access to the NancyContext and I can see the Items dictionary and that it contains a key called "OWIN_REQUEST_ENVIRONMENT". (I could also call the NancyContext.GetOwinEnvironment() extension, which gives the same result However, when I get that key it doesn't contain an actual IOwinContext.

It contains a lot of keys with information about Owin (some of the keys are owin.RequestPath, owin.RequestMethod, owin.CallCancelled, and more), but not an actual context object. And it is only really a dictionary with various keys, so I can't cast it to an IOwinContext either.

How can I get from a NancyContext to an IOwinContext object?


public class MyStartup
{
    public void Start()
    {
        var options = new StartOptions()
        options.Urls.Add(new Uri("http://*:8084"));
        options.AppStartup(this.GetType().AssemblyQualifiedName;
        var host = WebApp.Start(options, Configuration);
    }

    public void Configuration(IAppBuilder app)
    {
        app.UseNancy();
    }
}

public class MyModule : NancyModule
{
    Get["/", true] = async(x, ct) =>
    {
        var owinEnvironment = Context.GetOwinEnvironment();
        // Now what?
    }
}
Community
  • 1
  • 1
TheHvidsten
  • 4,028
  • 3
  • 29
  • 62

3 Answers3

1
var owinContext = new OwinContext(Context.GetOwinEnvironment());

example:

public class SecurityApi : NancyModule
{
    public SecurityApi()
    {
        Post["api/admin/register", true] = async (_, ct) =>
        {
            var body = this.Bind<RegisterUserBody>();

            var owinContext = new OwinContext(Context.GetOwinEnvironment());
            var userManager = owinContext.GetUserManager<ApplicationUserManager>();

            var user = new User {Id = Guid.NewGuid().ToString(), UserName = body.UserName, Email = body.Email};

            var result = await userManager.CreateAsync(user, body.Password);

            if (!result.Succeeded)
            {
                return this.BadRequest(string.Join(Environment.NewLine, result.Errors));
            }

            return HttpStatusCode.OK;
        };

    }
}
Marius
  • 9,208
  • 8
  • 50
  • 73
0

Actually, question that you mentioned has some tips that you probably missed.

For Nancy, you have to use NancyContext to get to the Items dictionary and look for the value corresponding to the key "OWIN_REQUEST_ENVIRONMENT". For SignalR, Environment property of IRequest gives you access to OWIN environment. Once you have the OWIN environment, you can create a new OwinContext using the environment.

So, once you called var owinEnvironment = Context.GetOwinEnvironment() and got the dictionary then you can create OwinContext (which is just wrapper for these dictionary values)

It has a constructor OwinContext(IDictionary<String, Object>) which, i guess, is what you need.

Also, you can get OwinContext from HttpContext:

// get owin context
var owinContext = HttpContext.Current.GetOwinContext();
// get user manager
var userManager = owinContext.GetUserManager<YourUserManager>();
Community
  • 1
  • 1
MaKCbIMKo
  • 2,800
  • 1
  • 19
  • 27
  • This worked to some degree, but I can't seem to get objects which have been registered with the OwinContext at an earlier occasion, like a AspNet.Identity.UserManager. In the `Configuration(IAppBuilder app)` method I also do a `app.CreatePerOwinContext(MyUserManager.Create);`. After creating a new OwinContext like you suggested, a call to `owinContext.GetUserManager()` returns NULL, so I'm not sure creating a new OwinContext actually contains what have previously been registered. – TheHvidsten May 01 '16 at 14:13
  • Well, I believe that you can access owin context from basic http context (I put an example) – MaKCbIMKo May 01 '16 at 16:11
  • I like the suggestion to use `HttpContext.Current` but unfortunately that's not available from a NancyModule class. At least not when I debug the entry point and see what's available. – TheHvidsten May 01 '16 at 17:01
  • Why isn't available? It must be. – MaKCbIMKo May 01 '16 at 17:20
  • I don't know. There's no autocompletion for HttpContext and if I type it in I get compiler errors. Typing the full name doesn't work either (System.Web.HttpContext, if memory serves me right) – TheHvidsten May 01 '16 at 19:07
  • It's a static class. Try to add `using System.Web;` or use the full name `System.Web.HttpContext.Current`. – MaKCbIMKo May 01 '16 at 19:13
  • As I mentioned above, using the full name doesn't work either. – TheHvidsten May 01 '16 at 19:26
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/110772/discussion-between-makcbimko-and-gthvidsten). – MaKCbIMKo May 01 '16 at 19:35
0

I ended up solving this by creating new Owin middleware. In the middleware you have access to the current Owin context, which gives you access to the Owin environment.

When you have access to the Owin environment it's simply a case of adding the Owin context to the environment. When the context is in the environment you can retrieve it in the NancyModule.

After retrieving it like this I also had access to the GetUserManager() method on the context so that I could get my AspNetIdentity manager (as I mentioned in a comment to another answer). Just remember that the middleware must be added before Nancy to the Owin pipeline.

Startup

public class Startup
{
    public void Start()
    {
        var options = new StartOptions()
        options.Urls.Add(new Uri("http://*:8084"));
        options.AppStartup(this.GetType().AssemblyQualifiedName;
        var host = WebApp.Start(options, Configuration);
    }

    public void Configuration(IAppBuilder app)
    {
        app.Use(typeof(OwinContextMiddleware));
        app.UseNancy();
    }
}

Middleware

public class OwinContextMiddleware : OwinMiddleware
{
    public OwinContextMiddleware(OwinMiddleware next)
        : base(next)
    {
    }

    public async override Task Invoke(IOwinContext context)
    {
        context.Environment.Add("Context", context);
        await Next.Invoke(context);
    }
}

NancyModule

public class MyModule : NancyModule
{
    public MyModule()
    {
        Get["/", true] = async(x, ct) =>
        {
            IDictionary<string, object> environment = Context.GetOwinEnvironment();
            IOwinContext context = (IOwinContext)environment["Context"]; // The same "Context" as added in the Middleware
        }
    }

Caveat

The middleware listed above is untested as the middleware I have is more complex and I haven't had the time to create a working example. I found a simple overview on how to create Owin middleware on this page.

TheHvidsten
  • 4,028
  • 3
  • 29
  • 62