【问题标题】:DryIoC / Web API 2 / OWIN and session managementDryIoC / Web API 2 / OWIN 和会话管理
【发布时间】:2016-02-21 01:55:39
【问题描述】:

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

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

我第一次放弃的尝试是使用 Ninject 并将 ISession 映射到提供程序工厂。虽然 Ninject 有它的优点(例如模块),但我不喜欢它的复杂性。我不知道如何从工厂访问请求对象。经过一番研究,我决定改用 DryIoC。

在下面的代码示例中,DryIoC 创建了一个单例会话(请参阅下面的重用)并将其注入我的 RootController。如果我在 Transient Scope 中注册 Session,显然每个请求都会获得一个会话。我设想调用“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
                });
        }
    }
}

【问题讨论】:

  • 我不太明白:你想要一个会话按什么
  • @FyodorSoikin Session per "something" :) 我不确定如何实现 DryIoC 在实例化控制器之前调用的工厂,我将在其中查找会话令牌并创建/查找关联的 ISession实例。然后 DryIoC 将使用它注入控制器。
  • 我认为你把这个复杂化了。如果遗留系统能够调用 API,那么它就有能力调用 API 来获取令牌。令牌存储在客户端,并在调用资源 API 时在标头中传递。查找 JWT 和 oAuth。 API 不应该有会话的概念。
  • @StephenBrickner 这只是更大系统的“服务器端”。还有其他模块(Node / Angular)将调用。JWT / oAuth在计划中,这正是我所说的“在标头中传递令牌”的意思,但是问题是我想用正确的会话注入来实例化控制器进去。为了注入正确的会话实例,我需要 DI 容器使用 http 上下文调用我,但我不知道该怎么做。

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


【解决方案1】:

首先,我不是 Web 人(而是 DryIoc 维护者),并且没有完全了解您想要实现的会话管理。但如果你想利用 request 来做这件事,你可以这样做:

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

顺便说一句,我已将此示例应用程序作为 DryIoc.WebApi.Owin.Sample app 推送到 DryIoc dev 分支。我希望你没问题。如果你不是,戳我。

【讨论】:

  • 回复:示例应用程序 - 非常好。回复:注入HttpRequestMessage,这就是我所缺少的!谢谢,我会对此进行试验并发表评论。现在这将是答案。感谢您的关注!
【解决方案2】:

如果您只想为这个特定接口执行一次,并且不介意手动管理其生命周期,则可以直接使用 RegisterDelegate 创建一个工厂:

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

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

或者,如果你想让“per token”这个想法适用于更广泛的组件,你可以实现一个自定义的IReuse,这是一个可以查找“当前范围”和“范围”的东西" 只是实例的缓存。

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

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

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

更好的方法是拥有一个名为ISessionManager 的单例组件,然后将其注入您的控制器,并让控制器调用ISessionManager.GetCurrentSession() 或其他东西。这样你就不会束缚自己:稍后你可以选择扩展 GetCurrentSession 所做的事情,或者你可以选择更改其返回类型以包含可能的错误(例如“无令牌”、“令牌过期”等),并让控制器将其报告给消费者。

想一想。

【讨论】:

  • 我考虑过会话管理器的方法,甚至写了一些测试代码。问题(据我了解,我可能错了)仍然在于: - 将 HTTP 标头映射到会话(如果有) - 将映射会话的实例注入所有控制器。那么这应该是自定义中间件的工作吗?
  • 不要注入会话实例。注入会话管理器本身。然后让控制器调用该实例上的方法以获取当前会话。
  • 正确,但可以进行多个会话。另外,会话管理器如何访问当前请求? ..哦,我明白了,您建议每个 REST 端点在开始时都会有一些样板代码 - “查找会话或退出”。叹息我试图隐藏它并让控制器忘记会话管理的“方式”。我们正在谈论一些控制器
  • 不,如果您不想,您不必拥有“样板代码”。会话管理器可以使用HttpContext.Current(错误选项)访问当前请求,或者通过DI 注入HttpContextBase(为此您必须使用RegisterDelegateReuse.Transient)。但是,对于 MVC 的工作方式,如果您希望您的应用程序设计良好,您几乎必须拥有样板代码。您可以使用在没有会话时仅返回错误的通用过滤器,但这仍然非常不灵活。
  • 哦,对了,错过了。您仍然可以将HttpRequestMessage 注入会话管理器。 DryIoc 应该负责为您检索当前的(尽管我自己从未尝试过)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-04-14
  • 2015-02-12
  • 2014-03-26
  • 2016-04-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多