4

克隆当前请求的 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?

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

4

5 回答 5

7

虽然“不可能”的答案是正确的,但还有一种替代方法比将值写入当前上下文然后重写回其原始状态要干净得多。解决方案是完全基于您选择的 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

于 2014-02-14T15:03:33.333 回答
2

不可能

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


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

于 2009-09-23T17:51:31.857 回答
0

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

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

于 2009-09-23T15:44:12.123 回答
0

对于 ASP.Net Core/.Net 5,以下将起作用(基于SignalR 的 ASP.Net Core 源代码,如果您需要更多功能,只需添加它们)。

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;            
}
于 2021-07-06T20:27:45.770 回答
-1

我用过

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

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

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

于 2009-09-23T15:48:21.133 回答