是否可以复制/克隆Web请求的HttpContext

时间:2009-09-23 15:36:15

标签: asp.net-mvc httpcontext cloning

克隆当前请求的HttpContext实例的最简单方法是什么?

我正在 Asp.net MVC v1 中开发应用。我将常规的PartialView功能升级为实际上具有非常相似的子控制器,但具有自己的上下文。使用PartialViews时,必须在主视图的控制器操作中填充局部视图的视图数据。我创建了自己的功能,可以从视图中调用控制器操作。这样我得到了:

  • 我不必在主视图的控制器操作中提供子视图的数据
  • 子控制器方法可以操纵更多封装的数据,而与其他视图/控制器没有任何关系

问题是每个子控制器请求都使用HttpContext。因此,当我在子控制器中设置一些HttpContext.Item时,它实际上填充了实际请求的HttpContext。

这就是我想要克隆HttpContext的原因。我已经在使用了:

HttpContext subContext = new HttpContext(request, response);
// what happened to Session, User, Items etc. properties?

但是除了请求和响应之外没有设置任何其他内容。但我可能还需要其他属性和集合......如会话,项目,用户......等。

5 个答案:

答案 0 :(得分:6)

虽然“不可能”的答案是正确的,但有一种替代方法比将值写入当前上下文然后重写回其原始状态要清晰得多。解决方案是完全基于您选择的URL创建一个新的HttpContext对象。

// A new request/response is constructed to using a new URL.
// The new response is using a StreamWriter with null stream as a backing stream 
// which doesn't consume resources

using (var nullWriter = new StreamWriter(Stream.Null))
{
    var newRequestUri = new Uri("http://www.somewhere.com/some-resource/");
    var newRequest = new HttpRequest("", newRequestUri.ToString(), newRequestUri.Query);

    var newResponse = new HttpResponse(nullWriter);
    var newContext = new HttpContextWrapper(new HttpContext(newRequest, newResponse));

    // Work with the new context here before it is disposed...
} 

参考:https://github.com/maartenba/MvcSiteMapProvider/issues/278#issuecomment-34905271

答案 1 :(得分:2)

不可能

我认为由于服务器会话状态,实际的深度克隆是不可能的。克隆还必须克隆此值,这是特定于Web服务器的内部资源,本质上是静态的,无法克隆。在这种情况下,Web服务器将具有多个Session对象。

解决方法
无论如何。解决方法是在实例化子控制器处理之前设置其他上下文值。处理完成后,我将值恢复为原始值。所以我实际上有以前的情境。

答案 2 :(得分:0)

ASP.NET MVC框架故意依赖于抽象类,所有成员都是虚拟的。这只是说 - 可扩展性。

控制器依赖于HttpContextBase,而不是HttpContext。也许你可以使你的子控制器也依赖于HttpContextBase,这样你就可以包装它。 我的2美分。

答案 3 :(得分:0)

我用过

<% Html.RenderAction("Action", "Controller"); %>

效果很好,允许我创建完全隔离/已封装的操作,而无需使用复杂的代码。这似乎提供相同的功能而没有相同的复杂性。

渲染视图是标准局部视图和控制器动作,就像任何其他视图一样。

答案 4 :(得分:0)

对于 ASP.Net Core/.Net 5,以下内容将起作用(基于 the ASP.Net Core source code for SignalR,如果您需要更多功能,只需添加它们)。

public static HttpContext Clone(this HttpContext httpContext, bool copyBody)
{
     var existingRequestFeature = httpContext.Features.Get<IHttpRequestFeature>();

     var requestHeaders = new Dictionary<string, StringValues>(existingRequestFeature.Headers.Count, StringComparer.OrdinalIgnoreCase);
     foreach (var header in existingRequestFeature.Headers)
     {
         requestHeaders[header.Key] = header.Value;
     }

     var requestFeature = new HttpRequestFeature
     {
         Protocol = existingRequestFeature.Protocol,
         Method = existingRequestFeature.Method,
         Scheme = existingRequestFeature.Scheme,
         Path = existingRequestFeature.Path,
         PathBase = existingRequestFeature.PathBase,
         QueryString = existingRequestFeature.QueryString,
         RawTarget = existingRequestFeature.RawTarget,
         Headers = new HeaderDictionary(requestHeaders),
     };

     if(copyBody)
     {
          // We need to buffer first, otherwise the body won't be copied
          // Won't work if the body stream was accessed already without calling EnableBuffering() first or without leaveOpen
          httpContext.Request.EnableBuffering();
          httpContext.Request.Body.Seek(0, SeekOrigin.Begin);
          requestFeature.Body = existingRequestFeature.Body;
     }

     var features = new FeatureCollection();
     features.Set<IHttpRequestFeature>(requestFeature);
        // Unless we need the response we can ignore it...
     features.Set<IHttpResponseFeature>(new HttpResponseFeature());
     features.Set<IHttpResponseBodyFeature>(new StreamResponseBodyFeature(Stream.Null));
     
     var newContext = new DefaultHttpContext(features);

     if (copyBody)
     {
         // Rewind for any future use...
         httpContext.Request.Body.Seek(0, SeekOrigin.Begin);
     }

        // Can happen if the body was not copied
     if(httpContext.Request.HasFormContentType && httpContext.Request.Form.Count != newContext.Request.Form.Count)
     {
         newContext.Request.Form = new Microsoft.AspNetCore.Http.FormCollection(httpContext.Request.Form.ToDictionary(f => f.Key, f => f.Value));
     }

     return newContext;            
}