我们有一个工作队列,用户可以向其中添加工作。当工作项被添加时,上下文是用户(HttpContext
)。但它是一个后台线程,轮询队列并按顺序逐个执行项。
我不能只存储User因为当HttpContext
被处理时Principal对象也会被处理
可以在worker中运行的代码需要Principal对PrincipalPermissions
等东西是正确的
同时,生命周期管理(IoC)在InRequest
范围内使用HttpContext
,是否可以用正确的主体等重新创建HttpContext
。
伪造HttpContext只是一个很好的生活时间管理功能,这我可以工作。但是我们的后端代码很大程度上依赖于线程的正确用户主体,因为我们使用它来验证用户是否有权访问系统的那一部分。我将标记为回答如果有人可以回答如何存储用户主体的身份,角色和IsAuthenticated状态,然后在另一个线程上使用
从HttpContext
中消费有状态数据的最佳实践是创建您自己的特定于应用程序的上下文,该上下文在构造函数中接受HttpContext
(依赖注入)。
您的业务逻辑永远不应该依赖于HttpContext
,而应该依赖于您的新应用程序特定上下文(可能已经使用来自HttpContext
的信息创建)。
这不仅可以解决你的上述问题,还可以增加你的代码的可测试性。
的例子:
public class MyApplicationContext
{
public IPrincipal ContextPrincipal { get; set; }
public MyApplicationContext(HttpContext httpContext)
{
// Store the current user principal & identity
ContextPrincipal = httpContext.User;
// Need to grab anything else from the HttpContext? Do it here!
// That could be cookies, Http request header values, query string
// parameters, session state variables, etc.
//
// Once you gather up any other stateful data, store it here in
// your application context object as the HttpRequest can't be passed
// to another thread.
}
}
public class MyHttpHandler : IHttpHandler
{
#region IHttpHandler Members
public bool IsReusable
{
// Return false in case your Managed Handler cannot be reused for another request.
// Usually this would be false in case you have some state information preserved per request.
get { return true; }
}
public void ProcessRequest(HttpContext context)
{
// Do some work on another thread using the ThreadPool
ThreadPool.QueueUserWorkItem(new WaitCallback(DoWork), new MyApplicationContext(context));
}
public void DoWork(object state)
{
// Grab our state info which should be an instance of an
// MyApplicationContext.
MyApplicationContext context = (MyApplicationContext) state;
// Assign this ThreadPool thread's current principal according
// to our passed in application context.
Thread.CurrentPrincipal = context.ContextPrincipal;
// Check if this user is authenticated.
if (context.ContextPrincipal.Identity.IsAuthenticated)
{
var userName = context.ContextPrincipal.Identity.Name;
}
// Check if this user is an administrator.
if (context.ContextPrincipal.IsInRole("Administrator"))
{
}
// Do some long-ish process that we need to do on the threadpool
// after the HttpRequest has already been responded to earlier.
//
// This would normally be some fancy calculation/math, data
// operation or file routines.
for (int i = 0; i < 30; i++)
{
Thread.Sleep(1000);
}
}
#endregion
}
IPrincipal
和IIdentity
接口都没有显式地提供dispose方法。所以他们都应该保持一个参考。然而,我还没有测试上面的代码,我只是为了这个问题而写的。
如果由于某些糟糕的设计,它们实际上依赖于底层数据库连接来查询角色成员,那么您只需在应用程序上下文的构造函数中更早地评估它,而HttpContext和asp.net表单身份验证提供程序仍然未处理/关闭。
你总是可以拆开主体和身份,重新创建GenericPrincipal
和GenericIdentity
的新实例,甚至创建实现IIdentity
的应用程序身份类。这里有很多定制/扩展的空间
public void TestMethod1()
{
System.Net.WebClient client = new System.Net.WebClient();
client.BaseAddress = "http://www.teejoo.com";
//Invoke your function here
client.OpenReadAsync(new Uri("http://www.teejoo.com/YourLogicalPage.aspx"));
//Pur your logical in your page, so you can use httpContext
client.OpenReadCompleted += new System.Net.OpenReadCompletedEventHandler(client_OpenReadCompleted);
}
void client_OpenReadCompleted(object sender, System.Net.OpenReadCompletedEventArgs e)
{
//to Check the response HERE
}
为什么不使用一个辅助类来保存所需的信息呢?你可以在web请求期间使用适当的值创建它,并将其作为参数传递给后台worker。
由于内部服务器会话状态,无法克隆HTTPContext
对象。即使可能,在真正的HTTP请求之外使用它只是为了检查值似乎也不是一个好的解决方案。