Search code examples
sessionasp.net-web-apiasp.net-web-api2owindryioc

DryIoC / Web API 2 / OWIN and session management


Trying to figure out a way to establish session management with DryIoC (v2.0.0-rc4build353), MS OWIN (v3.0.1, WebAPI2 (client v5.2.3) on VS2015, .Net 4.5.

I'm wrapping a rather elaborate legacy application with REST API. Strictly API server, no UI/MVC. I understand that it's impossible for me to go fully state-less because I have to keep a "model" open server-side. User must authenticate into the model as well. Hence concept of a Session came along. I want to use DI as much as possible.

My first discarded attempt was to use Ninject and map ISession to a provider factory. While Ninject has its pros (modules, for one), I didn't like the complexity of it. I can't figure out how to access request object from the factory. After some research I decided to switch to DryIoC.

In the code sample below DryIoC creates a singleton session (see Reuse below) and injects it into my RootController. If I register Session in Transient Scope, I obviously get a session per request. I envision that a call to, say, "api/login" will generate a token. Client will cache it and submit it with subsequent calls in a header (to enable API versioning as well).

Struggling with how to manage scoping.

EDIT: Clarification on what I think I need: I'm not sure how to implement a factory that DryIoC would call before instantiating a controller, where I'd lookup the session token and create/lookup associated ISession instance. DryIoC would then use it to inject into the controller.

EDIT: I am trying to hide all session management boilerplate and have all controllers be injected with an already initialized session. In case there's no session for this request, a separate route would return an error. Another thing to note is that a client has to acquire a token explicitly. There's no notion of a global "current" token or session.

using System;
using System.Web.Http;

using Microsoft.Owin.Hosting;
using Microsoft.Owin.Diagnostics;

using Owin;

using DryIoc;
using DryIoc.WebApi;

namespace di_test
{
    class Program
    {
        static void Main(string[] args)
        {
            var url = "http://localhost:8065";

            using (WebApp.Start<Startup>(url))
            {
                Console.WriteLine("Owin host started, any key to exit");
                Console.ReadKey();
            }
        }
    }

    class Startup
    {
        public void Configuration(IAppBuilder app_)
        {
            var config = new HttpConfiguration();
            config.MapHttpAttributeRoutes();

            config.Routes.MapHttpRoute(
                name: "default",
                routeTemplate: "{controller}"
               );

            var di = new DryIoc.Container();
            di.Register<ISession, Session>(Reuse.Singleton);
            di.WithWebApi(config);

            app_.UseWebApi(config);
            app_.UseErrorPage(ErrorPageOptions.ShowAll);
        }
    }

    public interface ISession
    {
        string Token { get; }
    }

    public class Session : ISession
    {
        string m_token = null;

        public Session()
        {
            Console.WriteLine("Session()");
        }

        public string Token => m_token ?? (m_token = Guid.NewGuid().ToString());
    }

    [RoutePrefix("api")]
    public class RootController : ApiController
    {
        readonly ISession m_session;

        public RootController(ISession session_)
        {
            m_session = session_;
        }

        [Route()]
        public IHttpActionResult GetApiRoot()
        {
            return Json(
                new
                {
                    type = "root",
                    token = m_session.Token
                });
        }
    }
}

Solution

  • First, I am not a Web guy (but DryIoc maintainer), and did not get fully what session management do you want to achieve. But if you want to utilize request for that, you can do that as following:

    public class Startup
    {
        public void Configuration(IAppBuilder app_)
        {
            var config = new HttpConfiguration();
            config.MapHttpAttributeRoutes();
    
            config.Routes.MapHttpRoute(
                name: "default",
                routeTemplate: "{controller}"
               );
    
            var di = new DryIoc.Container();
    
            // NOTE: Registers ISession provider to work with injected Request
            di.Register<ISession>(Made.Of(() => GetSession(Arg.Of<HttpRequestMessage>())));
    
            di.WithWebApi(config);
    
            app_.UseWebApi(config);
            app_.UseErrorPage(ErrorPageOptions.ShowAll);
        }
    
        public static ISession GetSession(HttpRequestMessage request)
        {
            // TODO: This is just a sample. Insert whatever session management logic you need.
            var session = new Session();
            return session;
        }
    }
    

    DryIoc will inject current HttpRequestMessage into GetSession. Then GetSession will be used to inject result session into controller.

    Here is more details how to use DryIoc factory methods.

    BTW I have pushed this sample app into DryIoc dev branch as a DryIoc.WebApi.Owin.Sample app. I hope you are OK with it. Poke me if you are not.