小編典典
更新:從2.2.2版本開始,HttpContextAccessor將上下文保留在一個對象中(據說是為了防止請求之間的混淆),這會影響當前解決方案…因此,您需要為IHttpContextAccessor(舊版本)提供以下實現并進行注冊作為一個單例:
public class HttpContextAccessor : IHttpContextAccessor
{
private static AsyncLocal _httpContextCurrent = new AsyncLocal();
HttpContext IHttpContextAccessor.HttpContext { get => _httpContextCurrent.Value; set => _httpContextCurrent.Value = value; }
}
using Microsoft.AspNetCore.Html;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc.Infrastructure;
using Microsoft.AspNetCore.Routing;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.IO;
using System.Threading.Tasks;
namespace Microsoft.AspNetCore.Mvc.Rendering
{
public static class HtmlHelperViewExtensions
{
public static IHtmlContent Action(this IHtmlHelper helper, string action, object parameters = null)
{
var controller = (string)helper.ViewContext.RouteData.Values["controller"];
return Action(helper, action, controller, parameters);
}
public static IHtmlContent Action(this IHtmlHelper helper, string action, string controller, object parameters = null)
{
var area = (string)helper.ViewContext.RouteData.Values["area"];
return Action(helper, action, controller, area, parameters);
}
public static IHtmlContent Action(this IHtmlHelper helper, string action, string controller, string area, object parameters = null)
{
if (action == null)
throw new ArgumentNullException("action");
if (controller == null)
throw new ArgumentNullException("controller");
var task = RenderActionAsync(helper, action, controller, area, parameters);
return task.Result;
}
private static async Task RenderActionAsync(this IHtmlHelper helper, string action, string controller, string area, object parameters = null)
{
// fetching required services for invocation
var serviceProvider = helper.ViewContext.HttpContext.RequestServices;
var actionContextAccessor = helper.ViewContext.HttpContext.RequestServices.GetRequiredService();
var httpContextAccessor = helper.ViewContext.HttpContext.RequestServices.GetRequiredService();
var actionSelector = serviceProvider.GetRequiredService();
// creating new action invocation context
var routeData = new RouteData();
foreach (var router in helper.ViewContext.RouteData.Routers)
{
routeData.PushState(router, null, null);
}
routeData.PushState(null, new RouteValueDictionary(new { controller = controller, action = action, area = area }), null);
routeData.PushState(null, new RouteValueDictionary(parameters ?? new { }), null);
//get the actiondescriptor
RouteContext routeContext = new RouteContext(helper.ViewContext.HttpContext) { RouteData = routeData };
var candidates = actionSelector.SelectCandidates(routeContext);
var actionDescriptor = actionSelector.SelectBestCandidate(routeContext, candidates);
var originalActionContext = actionContextAccessor.ActionContext;
var originalhttpContext = httpContextAccessor.HttpContext;
try
{
var newHttpContext = serviceProvider.GetRequiredService().Create(helper.ViewContext.HttpContext.Features);
if (newHttpContext.Items.ContainsKey(typeof(IUrlHelper)))
{
newHttpContext.Items.Remove(typeof(IUrlHelper));
}
newHttpContext.Response.Body = new MemoryStream();
var actionContext = new ActionContext(newHttpContext, routeData, actionDescriptor);
actionContextAccessor.ActionContext = actionContext;
var invoker = serviceProvider.GetRequiredService().CreateInvoker(actionContext);
await invoker.InvokeAsync();
newHttpContext.Response.Body.Position = 0;
using (var reader = new StreamReader(newHttpContext.Response.Body))
{
return new HtmlString(reader.ReadToEnd());
}
}
catch (Exception ex)
{
return new HtmlString(ex.Message);
}
finally
{
actionContextAccessor.ActionContext = originalActionContext;
httpContextAccessor.HttpContext = originalhttpContext;
if (helper.ViewContext.HttpContext.Items.ContainsKey(typeof(IUrlHelper)))
{
helper.ViewContext.HttpContext.Items.Remove(typeof(IUrlHelper));
}
}
}
}
}
它基于白羊座的反應。我更正了2.0版未編譯的內容,并添加了一些調整。當前的httpcontext和當前的actioncontext有2個美化的靜態值。在httpcontext中IHttpContextFactory.Create設置了一個,在代碼中設置了actioncontext。請注意,這取決于你使用的功能IActionContextAccessor,并IHttpContextAccessor可能不會被默認注冊,所以你可能需要將其添加在啟動:
services.AddSingleton();
services.AddSingleton();
HttpContext只是一個包裝器HttpContext.Features,因此,如果您在其中一個進行更改,則在另一個中也進行更改…我將在try
/ catch的最后部分重設我所了解的內容。
我IUrlHelper從Items緩存中刪除了,因為即使構建urlHelper的actionContext不同,該值也將被重用IUrlHelperFactory.GetUrlHelper。
Asp.net Core 2.0假設您不會這樣做,那么很有可能還有其他緩存的東西,因此我建議在使用此方法時要格外小心,如果不需要,請不要這樣做。
2020-05-19