Asp.net mvc 4 如何在WebAPI控制器(MVC4)中访问HttpServerUtility

Asp.net mvc 4 如何在WebAPI控制器(MVC4)中访问HttpServerUtility,asp.net-mvc-4,asp.net-web-api,httpcontext,Asp.net Mvc 4,Asp.net Web Api,Httpcontext,我需要访问MVC4apicController中控制器中的Server.MapPath(virtualPath)方法 答案通常是从ControllerContext.HttpContext.Server访问它。但是,与MVCController不同,ApicController的ControllerContext没有HttpContext 在Global.asax.cs中实例化的webapiapplication具有一个HttpContext元素(Context)。但是,与MVC3和更早版本不同

我需要访问MVC4apicController中控制器中的Server.MapPath(virtualPath)方法

答案通常是从ControllerContext.HttpContext.Server访问它。但是,与MVCController不同,ApicController的ControllerContext没有HttpContext

在Global.asax.cs中实例化的webapiapplication具有一个HttpContext元素(Context)。但是,与MVC3和更早版本不同,我无法找到从控制器访问WebAPI应用程序的方法。(早期版本将对它的引用存储在静态实例变量中。MVC4将删除该引用。)

此外,我还试图在单元测试中调用控制器方法时,找到一些在没有大量额外支架的情况下也能工作的方法。我想我可以使用HttpContext.Current(至少它可以编译)访问它,即使是在WebApi控制器中,但我不能为了测试而模仿它。(我这里讲的是单元测试,您可以直接调用控制器方法。我最近看过一些教程,其中您可以使用精简的HttpClient进行单元测试,从而测试整个堆栈。在我看来,这更像是低级集成测试。)


这看起来并没有那么难,但我已经花了几个小时在谷歌上搜索并尝试了一些东西,我的头因为撞到墙上而变得血淋淋。

我建议您将此功能抽象化:

public interface IMyDependency
{
    string MapPath(string path);
}
然后有一个实现:

public class MyConcreteDependency: IMyDependency
{
    public string MapPath(string path)
    {
        return HostingEnvironment.MapPath(path);
    }
}
最后,您的ApicController完全独立于所有静态方法调用,因此对单元测试非常友好:

public class MyController: ApiController
{
    private readonly IMyDependency dependency;
    public MyController(IMyDependency dependency)
    {
        this.dependency = dependency;
    }

    public HttpResponseMessage Get()
    {
        var path = this.dependency.MapPath("~/App_Data");
        ...
    }
}

对于
apicontroller
,为自己构建一个
DelegatingHandler
,并将您所有的好东西推到
request.Properties
。然后,无论您是在测试还是在运行live,都可以从请求中检索它们。这样做的好处是,您对控制器中的会话没有依赖性

消息处理程序
公共类ContextHandler:DelegatingHandler
{
受保护的覆盖任务SendAsync(HttpRequestMessage请求,System.Threading.CancellationToken CancellationToken)
{
//获取要添加到请求中的糖果
var goodies=/*调用goodieGoodieYumYum*/
//将我们的好东西添加到请求中
request.Properties.Add(Constants.RequestKey_Goodies,Goodies);
//传递给下一个处理程序
返回base.sendaync(请求、取消令牌);
}
}
控制器动作
var goodies=(List)Request.Properties[Constants.RequestKey_goodies];

可能的原因是,您可以将
ControllerContext.HttpContext.Server
包装到另一个类中进行测试,为什么不呢?
public class ContextHandler : DelegatingHandler
{
    protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, System.Threading.CancellationToken cancellationToken)
    {
        // get the goodies to add onto the request
        var goodies = /* call to goodieGoodieYumYum */


        // add our goodies onto the request
        request.Properties.Add(Constants.RequestKey_Goodies, goodies);

        // pass along to the next handler
        return base.SendAsync(request, cancellationToken);
    }
}
var goodies = (List<Goodie>)Request.Properties[Constants.RequestKey_Goodies];