将httpcontext添加到hangfire中



我是Hangfire的初学者,并期待使用Hangfire每月在我的Web应用程序中调用一些操作。但是这些动作需要httpcontext。

然后我的问题是:有什么方法可以在hangfire项目中添加(或创建)httpcontext?

我尝试使用Google,但没有合适的答案。感谢您的帮助!

我发现了一个简短的讨论。难过看到答案是"没办法的"。更新:参考https://discuss.hangfire.io/t/passing-site-url-to-hangfire-recurrent-jobs/2641

我也有类似的情况,该系统在很大程度上依赖会话,我采用了伪造httpcontext并传递会话变量的相同方法。

在我的方法中,我会收到一个可序列化的上下文,其中包含所有会话变量:

public static void MyMethod(Hangfire.Server.PerformContext context, Hangfire.IJobCancellationToken cancellationToken, 
        SerializeableHttpContext serializeableHttpContext, ... etc)
{
    using (var fakeContext = serializeableHttpContext.CreateFakeHttpContext()) {
    // ...
    }
}

在入选期间,我将当前上下文传递给我的可序列化上下文,这将捕获所有当前变量:

// null and null are injected by Hangfire
Hangfire.BackgroundJob.Enqueue(() => MyMethod(null, null, new SerializeableHttpContext(System.Web.HttpContext.Current), etc..);

这就是魔术发生的地方。这将节省所有会话变量,并将恢复它们。请注意,使用IDISPOSE很重要,因为您的下一个hangfire作业不想从上一个作业继承假的httpcontext,因此您需要清理httpcontext。

/// <summary>
/// This serializes HttpContext with primitive Session variables
/// </summary>
[Serializable]
public class SerializeableHttpContext
{
    public Uri RequestUrl { get; set; }
    public Dictionary<string, object> SessionVariables { get; set; }
    /// <summary>
    /// Given a real HttpContext (you can pass System.Web.HttpContext.Current), this saves all useful information 
    /// into this serializable class, so that you can later reuse (restore) a cloned fake HttpContext
    /// </summary>
    /// <param name="httpContext">You'll probably want to pass System.Web.HttpContext.Current</param>
    public SerializeableHttpContext(HttpContext httpContext)
    {
        this.RequestUrl = httpContext.Request.Url;
        // Save all Session variables
        this.SessionVariables = new Dictionary<string, object>();
        foreach (object objkey in httpContext.Session.Keys)
        {
            string key = objkey as string;
            if (key == null || httpContext.Session[key] == null)
                continue;
            Type type = httpContext.Session[key].GetType();
            if (type.IsPrimitive || type == typeof(string))
            {
                try
                {
                    // ignore if not serializable
                    object val = JsonConvert.DeserializeObject(JsonConvert.SerializeObject(httpContext.Session[key]));
                    this.SessionVariables.Add(key, httpContext.Session[key]);
                }
                catch (Exception) { }
            }
        }
    }
    /// This is for internal usage, when deserializing.
    public SerializeableHttpContext()
    {
    }
    /// <summary>
    /// Deserializes into a Fake HttpContext
    /// </summary>
    /// <returns></returns>
    protected HttpContext Deserialize()
    {
        var httpRequest = new HttpRequest("", this.RequestUrl.AbsoluteUri, "");
        var stringWriter = new StringWriter();
        var httpResponse = new HttpResponse(stringWriter);
        var httpContext = new HttpContext(httpRequest, httpResponse);
        var sessionContainer = new HttpSessionStateContainer("id", new SessionStateItemCollection(),
                                                new HttpStaticObjectsCollection(), 10, true,
                                                HttpCookieMode.AutoDetect,
                                                SessionStateMode.InProc, false);
        httpContext.Items["AspSession"] = typeof(HttpSessionState).GetConstructor(
                                    BindingFlags.NonPublic | BindingFlags.Instance,
                                    null, CallingConventions.Standard,
                                    new[] { typeof(HttpSessionStateContainer) },
                                    null)
                            .Invoke(new object[] { sessionContainer });
        // Restore session variables
        if (this.SessionVariables != null)
            foreach (string key in this.SessionVariables.Keys)
                httpContext.Session[key] = this.SessionVariables[key];
        // Restore context variables
        if (this.ContextVariables != null)
            foreach (string key in this.ContextVariables.Keys)
                httpContext.Items[key] = this.ContextVariables[key];
        return httpContext;
    }
    /// <summary>
    /// Deserializes this class back into a fake HttpContext, and automatically sets that into System.Web.HttpContext.Current
    /// Don't forget to DISPOSE this instance at the end, so that the Context is cleared (else Hangfire will reuse this thread with previous HttpContext)
    /// </summary>
    public FakeHttpContext CreateFakeHttpContext()
    {
        return new FakeHttpContext(this.Deserialize());
    }
    public class FakeHttpContext : IDisposable
    {
        HttpContext previousContext;
        public FakeHttpContext(HttpContext context)
        {
            previousContext = HttpContext.Current;
            HttpContext.Current = context;
        }
        public void Dispose()
        {
            HttpContext.Current = previousContext; // previousContext is probably null, but one might be using FakeHttpContexts even inside an existing web context
        }
    }
}

解决此问题3天后,我发现可以在hangfire内部创建一个假的httpcontext。在这个假的httpcontext中需要构建很多东西。但是,您只能初始化所需的属性,而无需定义全部。

大谢谢@jbl

最新更新