【问题标题】:Manually creating an HttpContext in ASP.NET Core 2.x在 ASP.NET Core 2.x 中手动创建 HttpContext
【发布时间】:2018-12-04 03:27:57
【问题描述】:

我正在尝试将 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 这样的库,它们不适合我的需要,因为它不能正确支持 @inject 指令。我认为我最好的解决方案是尝试模拟一个假的 HttpContext/ControllerContext 以传递给本机 ViewEngine。然而,当我创建一个新的DefaultHttpContext 时,我在here 附近得到一个 NullReferenceException,但是很难跟踪代码并找到它的来源。

有什么方法可以创建一个新的 HttpContext 吗?

【问题讨论】:

    标签: c# asp.net-core asp.net-core-mvc httpcontext


    【解决方案1】:

    您可以通过创建DefaultHttpContext 来模拟它,但是 MVC 需要一些不存在于根 DI 范围中的范围服务,因此您必须为您的渲染创建一个 ServiceProvider 范围。

    这是一个示例IHostedService,它呈现一个视图(我确实在带有 MVC 的 WebApplication 模板中运行了它):

    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)
        {
        }
    }
    

    注意:此示例基于在此处找到的博客文章,但已修改为在 IHostedService 中工作。 https://ppolyzos.com/2016/09/09/asp-net-core-render-view-to-string/

    【讨论】:

    • 感谢您的意见。我的代码已经非常相似,因为我从 OP 中省略了一些。原来删除“viewContext.RouteData = httpContext.GetRouteData()”就成功了!
    【解决方案2】:

    试试这个:

    public class YourClass 
    {
       private readonly IHttpContextAccessor _httpContextAccessor;
       public YourClass(IHttpContextAccessor httpContextAccessor)
       {
          _httpContextAccessor = httpContextAccessor;
       }
    
       public void YourMethod()
       {
          // access HttpContext with __httpContextAccessor.HttpContext
       }
    }
    

    然后在Startup类中注册IHttpContextAccessor如下:

    public void ConfigureServices(IServiceCollection services)
    {
        services.TryAddSingleton<IHttpContextAccessor, HttpContextAccessor>();
    
        // Or you can also register as follows
    
        services.AddHttpContextAccessor();
    }
    

    【讨论】:

    • 我试过了。 _httpContextAccessor.Context 为空,因为我的代码是从不需要 HTTP 请求的托管服务启动的
    • @Brad 不是_httpContextAccessor.Context,是__httpContextAccessor.HttpContext
    • 我的错,我忘了具体的字段是什么,但是当我尝试它时,它是空的
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-08-08
    • 2021-05-05
    • 2020-09-22
    • 1970-01-01
    • 2015-09-23
    • 2019-04-18
    相关资源
    最近更新 更多