DryIoC / Web API 2 / OWIN 和 session 管理
DryIoC / Web API 2 / OWIN and session management
试图找出一种方法来建立 session 管理与 DryIoC (v2.0.0-rc4build353), MS OWIN (v3.0.1, WebAPI2 (client v5.2.3) on VS2015,.Net 4.5.
我正在用 REST API 包装一个相当复杂的遗留应用程序。 严格API服务器,没有UI/MVC。我知道我不可能完全state-less因为我必须保留一个"model" 打开 server-side。用户也必须对模型进行身份验证。因此出现了 Session 的概念。我想尽可能多地使用 DI。
我放弃的第一个尝试是使用 Ninject 并将 ISession 映射到提供者工厂。虽然 Ninject 有其优点(模块,其中之一),但我不喜欢它的复杂性。我不知道如何从工厂访问请求 object。经过一番研究,我决定改用 DryIoC。
在下面的代码示例中,DryIoC 创建了一个单例 session(请参阅下面的重用)并将其注入到我的 RootController 中。如果我在 Transient Scope 中注册 Session,显然每个请求都会得到一个 session。我设想调用 "api/login" 将生成一个令牌。客户端将缓存它并在 header 中通过后续调用提交它(以启用 API 版本控制)。
为如何管理范围界定而苦苦挣扎。
编辑:澄清我认为我需要的东西:我不确定如何实现 DryIoC 在实例化控制器之前调用的工厂,我会在其中查找session 令牌和 create/lookup 关联的 ISession 实例。然后 DryIoC 将使用它注入控制器。
编辑:我正在尝试隐藏所有 session 管理样板文件,并让所有控制器注入已初始化的 session。如果此请求没有 session,单独的路由将 return 出错。另一件需要注意的事情是客户端必须显式获取令牌。没有全局 "current" 令牌或 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
});
}
}
}
如果您只想为这个特定接口执行一次,并且您不介意手动管理它的生命周期,您可以直接使用 RegisterDelegate
:
var sessions = new ConcurrentDictionary<Token, Session>();
di.RegisterDelegate<ISession>( _ => {
var token = GetCurrentToken();
return sessions.GetOrAdd( token, t => new Session( t ) );
} );
或者,如果你想把"per token"这个想法应用到更广泛的组件上,你可以实现一个自定义的IReuse
,这是一个可以查找的东西"current scope",而 "scope" 只是实例的缓存。
但是,我不推荐这种设计。这是这种逻辑的错误层。
举个例子:没有当前token怎么办?您当然可以抛出异常,但是没有地方可以捕获该异常——此时您仍然只是在构建组件图,因此客户端只会看到 WebApi 自动生成的异常消息。丑陋的。更不用说您无法进行任何恢复、记录事件等等。
另一个例子:在你开发的后期阶段,你很可能想在构建会话之前用那个令牌做一些事情。说,验证它,或检查它是否过期或已被召回,或其他什么。现在您正在向 DI 接线代码中添加更高级别的业务逻辑。丑陋的。无法维护。
更好的方法是拥有一个名为 ISessionManager
的单例组件,然后将其注入到您的控制器中,并让控制器调用 ISessionManager.GetCurrentSession()
或其他方式。这样你就不会束缚自己:稍后你可以选择扩展 GetCurrentSession
的功能,或者你可以选择更改其 return 类型以包含可能的错误(例如 "no token"、"token expired",等等),并让控制器将其报告给消费者。
想一想。
首先,我不是一个Web guy(而是DryIoc maintainer),并没有完全弄明白你想实现什么session management。但是如果你想利用请求,你可以这样做:
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 分支中。我希望你能接受。没有的话戳我