我的基于ServiceStack的应用程序使用内置身份验证功能,并以SelfHosted模式(AppHostHttpListenerLongRunningBase
)运行。
我正在使用NHibernate和Envers进行审计跟踪。 Envers可以提供可选的单例实例,它可以填充其他修订信息。我想在修订信息中存储当前用户的身份验证名称。
我需要访问 服务代码的当前请求(即“当前用户会话”)外部 ,在我的单身实例中。如何使用ServiceStack执行此操作?如何使其线程安全?
答案 0 :(得分:2)
我自己不使用NHibernate或Envers,所以也许只是在这里吐痰。我不认为当前的用户会话确实存在于服务范围之外。但是你应该能够传入Envers范围。我想你想做的是使用AppHost IoC将你的单例实例的引用传递给服务。
在您的AppHost
设置您的单例实例,然后将其注册到容器中,以便将其注入每个服务请求。
做类似的事情:
container.Register(c => singletonInstance).ReusedWithin(ReuseScope.None);
您需要扩展Service
以使用自定义基础:
public class MyServiceBaseWithEnversSupport : Service
{
public EnversSingletonInstanceType Envers { get; set; } // IoC will inject here
}
然后你的处理程序需要使用这个扩展的自定义Service
基础,所以像这样: CustomerHandler
只是一个例子,你的服务处理程序会有所不同
public class CustomerHandler : MyServiceBaseWithEnversSupport
{
public object Get(ListCustomers request)
{
// You can then access the instance in the scope of the request
// So you now have access to the current user identity
Envers.Username = Session.Username; // Just an example modify as required.
}
}
您可以自动填充值,以节省必须通过设置自定义ServiceRunner
来设置每个操作处理程序中的值。
创建自定义ServiceRunner
:
public class ServiceRunner<T> : ServiceStack.ServiceHost.ServiceRunner<T>
{
public ServiceRunner(IAppHost appHost, ActionContext actionContext) : base(appHost, actionContext)
{
}
public override object Execute(IRequestContext requestContext, object instance, T request)
{
// Check if the instance is of type MyServiceBaseWithEnversSupport
var ms = instance as MyServiceBaseWithEnversSupport;
// If the request is not using the MyServiceBaseWithEnversSupport, then allow it to run, as normal.
if(ms == null)
return base.Execute(requestContext, instance, request);
// Access the Envers object, set using the Session Information
ms.Envers.Username = ms.Session.Username;
return base.Execute(requestContext, ms, request);
}
}
通过将应用程序添加到AppHost
:
public override IServiceRunner<TRequest> CreateServiceRunner<TRequest>(ActionContext actionContext)
{
return new ServiceRunner<TRequest>(this, actionContext);
}
对于Enver的单例对象类型以及调用对象来设置数据的正确属性或方法,对不起,部分有点模糊,但我假设您可以用适当的值替换。
正如我所说,我不熟悉NHibernate或Envers,所以这可能会关闭,但至少对于ServiceStack方面可能有所帮助
我认为对于您的服务的每个请求都会发生以下情况:
您是否考虑过为监听器中的ServiceStack和Envers事件处理程序创建一个全局的ThreadStatic
static
变量?
public static class Global
{
[ThreadStatic]
public static string Username;
}
然后在ServiceStack中,在身份验证点设置Username
的值,这将在侦听器的处理程序之前发生。然后在侦听器处理程序中读取Global.Username
的值。该值是线程安全的,仅存在于请求范围内。
注意:我假设NHibernate / Envers在请求线程上运行,并且它不会为每个请求产生其他工作线程。