ASP.NET MVC,HttpContext.Current 在模拟请求时为空

ASP.NET MVC, HttpContext.Current is null while mocking a request

public async Task <ActionResult>Select(DWorkingTimeSelection dto) { 
    var session = HttpUtils.GetSeesion<User>("USER"); 
} 

public static T GetSeesion<T>(string key) where T : class
{ 
    if (HttpContext.Current == null) 
    { 
        return default(T);
    }
     return HttpContext.Current.Session[key] as T; 
}

public async Task <ActionResult>Select(DWorkingTimeSelection dto) { 
    var session = HttpUtils.GetSeesion<User>("USER"); 
} 

public static T GetSeesion<T>(string key) where T : class
{ 
    if (HttpContext.Current == null) 
    { 
        return default(T);
    } 
    return HttpContext.Current.Session[key] as T; 
}

我使用 nunti 模拟一个请求。 然后我将 SessionStateItemCollection 添加到 Controller 的 ControllerContext 中。 我发现 HttpContext.Current 是 null 但 Controller 的 Session[] 不为 null,因为它来自 Controller 的 ControllerContext。 那么我应该怎么做才能在模拟请求时避免 HttpContext.Current 为空

您可以模拟 HttpContext:

    public static HttpContext FakeHttpContext(HttpRequest request)
    {
        var stringWriter = new StringWriter();
        var httpResponce = new HttpResponse(stringWriter);
        var httpContext = new HttpContext(request, httpResponce);

        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 });

        return httpContext;
    }

    [TestMethod]
    public void ActionTest()
    {
        var request = new HttpRequest(string.Empty, "url to the action that you are testing", string.Empty)
        {
            RequestType = "GET"
        };
        HttpContext.Current = FakeHttpContext(request);

        var controller = new YourController();
        //You need the get a Result property since it is an async action
        var result = controller.ActionToTest(//the parameters that your action expects).Result;
        Assert.IsNotNull(result);
    }

编辑(回答您评论中的问题)

为了获得会话,您需要调用 HttpContext.Current.Session 而不是 HttpContext.Current.Session["id"],因为 HttpContext.Current.Session["id"] 试图从您的会话中获取 id 密钥。 一旦将它存储在那里,就可以通过此调用使用它。你可以测试一下:

HttpContext.Current.Session["id"] = 5;
Assert.AreEqual(HttpContext.Current.Session["id"],5);

关于 httpContext.Items 声明:

(来自 MSDN)HttpContext.Items 是一个 key/value 集合,可用于在 HTTP 期间在 IHttpModule 接口和 IHttpHandler 接口之间组织和共享数据要求。

您要求的语句只是通过使用反射(因为它只有内部构造函数)创建一个新的 HttpSessionState 对象,将其与您之前创建的 HttpSessionStateContainer 相关联并存储它在 AspSession 下的 HttpContext.Items 中。有趣的是 HttpContext.Current.Session 实际上是 HttpContext.Items["AspSession"] 的捷径。因此,将 HttpSessionState 对象分配给 AspSession 键是使 HttpContext.Current.Session 起作用的前提。

好吧,在像 Alex Art 所说的那样模拟 HttpContext 之后。

调用下面的方法就可以了

var sessionItems = new SessionStateItemCollection();
        sessionItems["SessionKey"] = new MyCustomObject();
        SessionStateUtility.AddHttpSessionStateToContext(fakeHttpContext,
            new HttpSessionStateContainer(SessionNameStorage.Suser,
                          sessionItems,
                          new HttpStaticObjectsCollection(),
                          20000,
                          true,
                          HttpCookieMode.AutoDetect,
                          SessionStateMode.InProc,
                          false
                      ));