在 ASP.NET Core 2.x 中手动创建 HttpContext
Manually creating an HttpContext in ASP.NET Core 2.x
我正在尝试将 Razor 视图呈现为来自托管服务的字符串。通过使用 IRazorViewEngine
我可以使用类似以下的内容将视图呈现为字符串:
_viewEngine.FindView(actionContext, viewName, false);
var viewDictionary = new ViewDataDictionary(new EmptyModelMetadataProvider(), new ModelStateDictionary())
{
Model = model
};
var viewContext = new ViewContext(
actionContext,
viewResult.View,
viewDictionary,
new TempDataDictionary(actionContext.HttpContext, _tempDataProvider),
sw,
new HtmlHelperOptions()
);
viewContext.RouteData = httpContext.GetRouteData(); //set route data here
await viewResult.View.RenderAsync(viewContext);
然而,由于缺少 HttpContext
而未从 Controller
中调用它时,它就会分崩离析。我已经尝试手动构建 HttpContext,但是我在 Microsoft Mvc 代码深处遇到了很多错误和空异常,这非常难以调试。我试过像 RazorLight which don't suit my needs because it doesn't properly support the @inject
directive. I think my best solution is to try and mock up a fake HttpContext/ControllerContext to pass to the native ViewEngine. However, when I create a new DefaultHttpContext
, I get a NullReferenceException around here 这样的库,但是很难跟踪代码并找到它的来源。
有没有办法创建一个新的HttpContext?
试试这个:
public class YourClass
{
private readonly IHttpContextAccessor _httpContextAccessor;
public YourClass(IHttpContextAccessor httpContextAccessor)
{
_httpContextAccessor = httpContextAccessor;
}
public void YourMethod()
{
// access HttpContext with __httpContextAccessor.HttpContext
}
}
然后在Startupclass中注册IHttpContextAccessor
如下:
public void ConfigureServices(IServiceCollection services)
{
services.TryAddSingleton<IHttpContextAccessor, HttpContextAccessor>();
// Or you can also register as follows
services.AddHttpContextAccessor();
}
您可以通过创建一个 DefaultHttpContext
来模拟它,但是 MVC 需要一些在根 DI 范围中不存在的范围内的服务,因此您必须为您的渲染创建一个 ServiceProvider
范围。
这是一个呈现视图的示例 IHostedService
(我在 WebApplication 模板中用 MVC 做了 运行):
public class ViewRenderService : IHostedService
{
private readonly IRazorViewEngine _razorViewEngine;
private readonly ITempDataProvider _tempDataProvider;
private readonly IServiceProvider _serviceProvider;
public ViewRenderService(IRazorViewEngine razorViewEngine,
ITempDataProvider tempDataProvider,
IServiceProvider serviceProvider)
{
_razorViewEngine = razorViewEngine;
_tempDataProvider = tempDataProvider;
_serviceProvider = serviceProvider;
}
public async Task<string> RenderToStringAsync(string viewName, object model)
{
using (var requestServices = _serviceProvider.CreateScope())
{
var httpContext = new DefaultHttpContext { RequestServices = requestServices.ServiceProvider };
var routeData = new RouteData();
routeData.Values.Add("controller", "Home");
var actionContext = new ActionContext(httpContext, routeData, new ActionDescriptor());
using (var sw = new StringWriter())
{
var viewResult = _razorViewEngine.FindView(actionContext, viewName, false);
if (viewResult.View == null)
{
throw new ArgumentNullException($"{viewName} does not match any available view");
}
var viewDictionary = new ViewDataDictionary(new EmptyModelMetadataProvider(), new ModelStateDictionary())
{
Model = model
};
var viewContext = new ViewContext(
actionContext,
viewResult.View,
viewDictionary,
new TempDataDictionary(actionContext.HttpContext, _tempDataProvider),
sw,
new HtmlHelperOptions()
);
await viewResult.View.RenderAsync(viewContext);
return sw.ToString();
}
}
}
public async Task StartAsync(CancellationToken cancellationToken)
{
var html = await RenderToStringAsync("About", null);
return;
}
public async Task StopAsync(CancellationToken cancellationToken)
{
}
}
注意:此示例基于此处找到的博客 post,但经过修改后可在 IHostedService
中使用。
https://ppolyzos.com/2016/09/09/asp-net-core-render-view-to-string/
我正在尝试将 Razor 视图呈现为来自托管服务的字符串。通过使用 IRazorViewEngine
我可以使用类似以下的内容将视图呈现为字符串:
_viewEngine.FindView(actionContext, viewName, false);
var viewDictionary = new ViewDataDictionary(new EmptyModelMetadataProvider(), new ModelStateDictionary())
{
Model = model
};
var viewContext = new ViewContext(
actionContext,
viewResult.View,
viewDictionary,
new TempDataDictionary(actionContext.HttpContext, _tempDataProvider),
sw,
new HtmlHelperOptions()
);
viewContext.RouteData = httpContext.GetRouteData(); //set route data here
await viewResult.View.RenderAsync(viewContext);
然而,由于缺少 HttpContext
而未从 Controller
中调用它时,它就会分崩离析。我已经尝试手动构建 HttpContext,但是我在 Microsoft Mvc 代码深处遇到了很多错误和空异常,这非常难以调试。我试过像 RazorLight which don't suit my needs because it doesn't properly support the @inject
directive. I think my best solution is to try and mock up a fake HttpContext/ControllerContext to pass to the native ViewEngine. However, when I create a new DefaultHttpContext
, I get a NullReferenceException around here 这样的库,但是很难跟踪代码并找到它的来源。
有没有办法创建一个新的HttpContext?
试试这个:
public class YourClass
{
private readonly IHttpContextAccessor _httpContextAccessor;
public YourClass(IHttpContextAccessor httpContextAccessor)
{
_httpContextAccessor = httpContextAccessor;
}
public void YourMethod()
{
// access HttpContext with __httpContextAccessor.HttpContext
}
}
然后在Startupclass中注册IHttpContextAccessor
如下:
public void ConfigureServices(IServiceCollection services)
{
services.TryAddSingleton<IHttpContextAccessor, HttpContextAccessor>();
// Or you can also register as follows
services.AddHttpContextAccessor();
}
您可以通过创建一个 DefaultHttpContext
来模拟它,但是 MVC 需要一些在根 DI 范围中不存在的范围内的服务,因此您必须为您的渲染创建一个 ServiceProvider
范围。
这是一个呈现视图的示例 IHostedService
(我在 WebApplication 模板中用 MVC 做了 运行):
public class ViewRenderService : IHostedService
{
private readonly IRazorViewEngine _razorViewEngine;
private readonly ITempDataProvider _tempDataProvider;
private readonly IServiceProvider _serviceProvider;
public ViewRenderService(IRazorViewEngine razorViewEngine,
ITempDataProvider tempDataProvider,
IServiceProvider serviceProvider)
{
_razorViewEngine = razorViewEngine;
_tempDataProvider = tempDataProvider;
_serviceProvider = serviceProvider;
}
public async Task<string> RenderToStringAsync(string viewName, object model)
{
using (var requestServices = _serviceProvider.CreateScope())
{
var httpContext = new DefaultHttpContext { RequestServices = requestServices.ServiceProvider };
var routeData = new RouteData();
routeData.Values.Add("controller", "Home");
var actionContext = new ActionContext(httpContext, routeData, new ActionDescriptor());
using (var sw = new StringWriter())
{
var viewResult = _razorViewEngine.FindView(actionContext, viewName, false);
if (viewResult.View == null)
{
throw new ArgumentNullException($"{viewName} does not match any available view");
}
var viewDictionary = new ViewDataDictionary(new EmptyModelMetadataProvider(), new ModelStateDictionary())
{
Model = model
};
var viewContext = new ViewContext(
actionContext,
viewResult.View,
viewDictionary,
new TempDataDictionary(actionContext.HttpContext, _tempDataProvider),
sw,
new HtmlHelperOptions()
);
await viewResult.View.RenderAsync(viewContext);
return sw.ToString();
}
}
}
public async Task StartAsync(CancellationToken cancellationToken)
{
var html = await RenderToStringAsync("About", null);
return;
}
public async Task StopAsync(CancellationToken cancellationToken)
{
}
}
注意:此示例基于此处找到的博客 post,但经过修改后可在 IHostedService
中使用。
https://ppolyzos.com/2016/09/09/asp-net-core-render-view-to-string/