如何从ASP.NET MVC 1中的HttpContextBase对象获取HttpContext对象?

161

我正在使用一些与WebForms/MVC无关的工具,需要根据HttpContextBase对象的引用获取HttpContext实例。我不能使用HttpContext.Current,因为我需要异步工作时也能正常工作(HttpContext.Current在异步请求期间返回null)。我知道有HttpContextWrapper,但它是相反的。

3个回答

259

最简单的方法是获取应用程序ApplicationInstance,并使用其Context属性:

// httpContextBase is of type HttpContextBase
HttpContext context = httpContextBase.ApplicationInstance.Context;

(感谢Ishmael Smyrnow在评论中提出这一点)

原始答案:

您可以这样做,特别是如果您手头的HttpContextBase实例在运行时是HttpContextWrapper类型。以下示例说明了如何执行此操作。它假定您有一个名为Foo的方法,该方法接受上下文作为HttpContextBase,但然后需要调用第三方程序集中的方法(您可能没有幸运修改该程序集),该方法期望将上下文类型化为HttpContext

void Foo(HttpContextBase context) 
{
    var app = (HttpApplication) context.GetService(typeof(HttpApplication));
    ThirdParty.Bar.Baz(app.Context);
}

// Somewhere in assembly and namespace ThirdParty,
// in a class called Bar, there is Baz expecting HttpContext:

static void Baz(HttpContext context) { /* ... */ }

HttpContextBase有一个名为GetService的方法,因为它支持IServiceProviderHttpContextWrapperGetService覆盖委托给包装的HttpContext实例的GetService实现。 HttpContextGetService实现允许您查询通常的对象,例如HttpApplicationHttpRequestHttpResponse等。这恰好是由于HttpApplication具有名为Context的属性,并返回HttpContext的实例。因此,通过通过GetService请求HttpApplication从而读取返回的HttpApplication实例的Context属性,就可以获取包装的HttpContext实例。

HttpContextBase不同,GetServiceHttpContext中不会出现为公共成员,但这是因为HttpContext明确实现了IServiceProvider.GetServiceHttpContextBase没有。

请记住,Foo不再可测试,因为它依赖于能够在测试期间解包基础的HttpContext,而这几乎是不可能进行伪造/存根的。然而,本答案的重点是回答问题,“如何从HttpContextBase获取HttpContext对象?”,字面上来说。所示技术在那些你发现自己被夹在不一定有奢侈修改的组件之间的情况下非常有用。

3
非常有趣的答案,我刚在MVC中使用它将HttpContext从错误过滤器传递给ELMAH进行日志记录。 - Chris Marisic
1
好的答案,我敢打赌那需要花费一些时间来查找? :) - longhairedsi
太棒了!我特别在我的Elmah到MVC控制器中使用了它 https://github.com/alexanderbeletsky/elmah.mvc.controller/blob/master/Areas/Admin/Controllers/ElmahController.cs - Alexander Beletsky
38
使用HttpContextBase对象,你可以调用context.ApplicationInstance.Context吗? - Ishmael Smyrnow
@IshmaelSmyrnow 糟糕,不知道在所有由 HttpContextBase 返回的 Http*Base 变体中是如何错过它的。:P 我会更新答案的。 - Atif Aziz

31

您可以:

var abstractContext = new System.Web.HttpContextWrapper(System.Web.HttpContext.Current);

18
这条路不对,这会在你有上下文时给你基础,而不是相反。 - Chris Marisic

14

你不能这样做。

HttpContextBase 的整个目的是抽象掉对具体 HttpContext 类的依赖。虽然它可能包含一个具体的 HttpContext (例如在 httpContextWrapper 中的情况),但其他实现可能完全与 HttpContext 无关。

您最好的选择是定义一个自定义的抽象工厂,可以为您获取 HttpContextBase,因为您始终可以将具体的 HttpContext 包装在 HttpContextWrapper 中。


14
顺便提一下,这样做太糟糕了 - 我理解希望为单元测试设置一个框架,并且我理解抽象层次,但是当你想要使用现有的 ASP.NET 类(这些类期望 HttpContext)时,你应该怎么办呢?(比如http://msdn.microsoft.com/en-us/library/system.web.sessionstate.sessionidmanager.aspx) - marq
测试 IHttpHandler 实现时,您最终不得不添加大量的抽象(响应包装器)到单元测试中,这并不感觉正确,但最终成为唯一的选择。 - Chris S

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接