DryIoC / Web API 2 / OWIN和会话管理

时间:2015-11-19 04:14:58

标签: session asp.net-web-api asp.net-web-api2 owin dryioc

尝试找出一种方法,在VS2015,.Net 4.5上使用DryIoC(v2.0.0-rc4build353),MS OWIN(v3.0.1,WebAPI2(客户端v5.2.3))建立会话管理。

我用REST API包装了一个相当复杂的遗留应用程序。 严格的API服务器,没有UI / MVC。我知道我完全无状态是不可能的,因为我必须保留一个"模型"打开服务器端。用户也必须在模型中进行身份验证。因此会议的概念出现了。我想尽可能多地使用DI。

我的第一次被抛弃的尝试是使用Ninject并将ISession映射到提供者工厂。虽然Ninject有它的专业(模块,一个),但我并不喜欢它的复杂性。我无法弄清楚如何从工厂访问请求对象。经过一番研究后,我决定改用DryIoC。

在下面的代码示例中,DryIoC创建一个单例会话(请参阅下面的重用)并将其注入我的RootController。如果我在Transient Scope中注册Session,我显然会在每个请求中获得一个会话。我设想打电话给#34; api / login"将生成一个令牌。客户端将对其进行缓存并通过标头中的后续调用将其提交(以启用API版本控制)。

苦苦思索如何管理范围。

编辑:澄清我认为我需要的内容:我不确定如何在实例化控制器之前实现DryIoC会调用的工厂,我会在其中查找会话令牌和创建/查找关联的ISession实例。 DryIoC然后将其用于注入控制器。

编辑:我试图隐藏所有会话管理样板,并为所有控制器注入已初始化的会话。如果此请求没有会话,则单独的路由将返回错误。另一件需要注意的事情是客户端必须明确获取令牌。没有一个全球性的当前"令牌或会话。

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
                });
        }
    }
}

2 个答案:

答案 0 :(得分:3)

首先,我不是一个Web人(但是DryIoc维护者),并没有完全了解你想要实现的会话管理。但是如果你想利用它的请求,你可以这样做:

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会将当前HttpRequestMessage注入GetSession。然后GetSession将用于将结果会话注入控制器。

以下是有关如何使用DryIoc factory methods的详细信息。

BTW我已将此示例应用程序推送到DryIoc dev 分支DryIoc.WebApi.Owin.Sample app。我希望你对它好。如果你不是,请戳我。

答案 1 :(得分:0)

如果您只想为此特定界面执行此操作一次,并且您不介意手动管理其生命周期,则可以使用RegisterDelegate创建工厂:

var sessions = new ConcurrentDictionary<Token, Session>();

di.RegisterDelegate<ISession>( _ => {
    var token = GetCurrentToken();
    return sessions.GetOrAdd( token, t => new Session( t ) );
} );

或者,如果你想要这个&#34;每个令牌&#34;适用于广泛的组件,您可以实现自定义IReuse,这是一个可以查找&#34;当前范围&#34;和&#34;范围&#34;只是实例的缓存。

然而,我不推荐这种设计。这是这种逻辑的错误层。

一个例子:当没有当前令牌时你会怎么做?你当然可以抛出一个异常,但是没有地方可以捕获这个异常 - 你仍然只是在那时构建你的组件图, - 所以客户端只会看到WebApi自动生成的异常消息。丑陋。更不用说您无法进行任何恢复,记录事件等等。

另一个例子:在开发的后期阶段,您很可能希望在构建会话之前对该令牌执行某些操作。说,验证它,或检查它是否已过期或已被召回,或其他什么。现在,您正在为DI接线代码添加更高级别的业务逻辑。丑陋。难以维护。

更好的方法是使用名为ISessionManager的单例组件,然后将其注入控制器,并让控制器调用ISessionManager.GetCurrentSession()或其他东西。那么你就不会把自己束缚自己:以后你可以选择扩展GetCurrentSession所做的事情,或者你可以选择更改其返回类型以包含可能的错误(例如&#34;没有令牌&#34;, &#34;令牌已过期&#34;等),并让控制器向消费者报告。

想一想。