首页 文章

在.NET Core中将视图返回为字符串

提问于
浏览
26

我发现了一些文章如何在ASP.NET中将视图返回到字符串,但无法转换为能够使用.NET Core运行它

public static string RenderViewToString(this Controller controller, string viewName, object model)
{
    var context = controller.ControllerContext;
    if (string.IsNullOrEmpty(viewName))
        viewName = context.RouteData.GetRequiredString("action");

    var viewData = new ViewDataDictionary(model);

    using (var sw = new StringWriter())
    {
        var viewResult = ViewEngines.Engines.FindPartialView(context, viewName);
        var viewContext = new ViewContext(context, viewResult.View, viewData, new TempDataDictionary(), sw);
        viewResult.View.Render(viewContext, sw);

        return sw.GetStringBuilder().ToString();
    }
}

假设能够使用以下控制器从控制器调用:

var strView = this.RenderViewToString("YourViewName", yourModel);

当我尝试将上述内容运行到.NET Core时,我遇到了很多编译错误 .

我试图将其转换为与.NET Core一起使用,但是失败了,任何人都可以帮助提及 using .. 所需的 using .. 和所需的 "dependencies": { "Microsoft.AspNetCore.Mvc": "1.1.0", ... }, .

一些其他示例代码是hereherehere

NOTE 我需要解决方案将视图转换为.NET Core中的 string ,无论转换的代码是什么,或者是另一种方法 .

7 回答

  • 1

    微软在https://docs.microsoft.com/en-us/aspnet/core/mvc/controllers/testing上有一篇关于控制器测试的优秀文章

    返回ViewResult后,您可以获取字符串内容

    var strResult = ViewResult.Content

  • 2

    感谢Paris Polyzos和他的article .

    我在这里重新发布他的代码,以防原始帖子因任何原因被删除 .

    在文件 viewToString.cs 中创建 Service ,如下代码:

    using System;
    using System.IO;
    using System.Threading.Tasks;
    using Microsoft.AspNetCore.Http;
    using Microsoft.AspNetCore.Mvc;
    using Microsoft.AspNetCore.Mvc.Abstractions;
    using Microsoft.AspNetCore.Mvc.ModelBinding;
    using Microsoft.AspNetCore.Mvc.Razor;
    using Microsoft.AspNetCore.Mvc.Rendering;
    using Microsoft.AspNetCore.Mvc.ViewFeatures;
    using Microsoft.AspNetCore.Routing;
     
    namespace WebApplication.Services
    {
        public interface IViewRenderService
        {
            Task<string> RenderToStringAsync(string viewName, object model);
        }
     
        public class ViewRenderService : IViewRenderService
        {
            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)
            {
                var httpContext = new DefaultHttpContext { RequestServices = _serviceProvider };
                var actionContext = new ActionContext(httpContext, new 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();
                }
            }
        }
    }
    
    • 将服务添加到 Startup.cs 文件中,如下所示:
    using WebApplication.Services;
    
    public void ConfigureServices(IServiceCollection services)
    {
        ...
        services.AddScoped<IViewRenderService, ViewRenderService>();
     }
    

    "preserveCompilationContext": true 添加到 project.json 中的 buildOptions ,因此该文件如下所示:

    {
      "version": "1.0.0-*",
      "buildOptions": {
        "debugType": "portable",
        "emitEntryPoint": true,
        "preserveCompilationContext": true
      },
      "dependencies": {
        "Microsoft.AspNetCore.Server.Kestrel": "1.0.1",
        "Microsoft.AspNetCore.Mvc": "1.0.1"
      },
      "frameworks": {
        "netcoreapp1.0": {
          "dependencies": {
            "Microsoft.NETCore.App": {
              "type": "platform",
              "version": "1.0.1"
            }
          },
          "imports": "dnxcore50"
        }
      }
    }
    
    • 定义你 model ,例如:
    public class InviteViewModel {
        public string   UserId {get; set;}
        public string   UserName {get; set;}
        public string   ReferralCode {get; set;}
        public int  Credits {get; set;}
    }
    
    • 创建 Invite.cshtml 例如:
    @{
        ViewData["Title"] = "Contact";
    }
    @ViewData["Title"].
    user id: @Model.UserId
    
    • Controller 中:

    一个 . 在开头定义以下内容:

    private readonly IViewRenderService _viewRenderService;
     
    public RenderController(IViewRenderService viewRenderService)
    {
        _viewRenderService = viewRenderService;
    }
    

    湾使用模型调用并返回视图,如下所示:

    var result = await _viewRenderService.RenderToStringAsync("Email/Invite", viewModel);
    return Content(result);
    

    C . FULL控制器示例可能如下:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Threading.Tasks;
    using Microsoft.AspNetCore.Mvc;
    
    using WebApplication.Services;
    
    namespace WebApplication.Controllers
    {
    [Route("render")]
    public class RenderController : Controller
    {
        private readonly IViewRenderService _viewRenderService;
     
        public RenderController(IViewRenderService viewRenderService)
        {
            _viewRenderService = viewRenderService;
        }
     
        [Route("invite")]
        public async Task<IActionResult> RenderInviteView()
        {
            ViewData["Message"] = "Your application description page.";
            var viewModel = new InviteViewModel
            {
                UserId = "cdb86aea-e3d6-4fdd-9b7f-55e12b710f78",
                UserName = "Hasan",
                ReferralCode = "55e12b710f78",
                Credits = 10
            };
     
            var result = await _viewRenderService.RenderToStringAsync("Email/Invite", viewModel);
            return Content(result);
        }
    }
    
    public class InviteViewModel {
            public string   UserId {get; set;}
            public string   UserName {get; set;}
            public string   ReferralCode {get; set;}
            public int  Credits {get; set;}
    } 
    }
    
  • 1

    如果像我一样你有许多需要这个的控制器,比如报告网站,重复这个代码并不是很理想,甚至注入或调用另一个服务似乎并不合适 .

    所以我已经制作了我自己的上述版本,但有以下不同之处:

    • 模型强类型
      查找视图时

    • 错误检查

    • 将视图呈现为部分或页面的能力

    • asynchronus

    • 实现为控制器扩展

    • 不需要DI

    using Microsoft.AspNetCore.Mvc;
    using Microsoft.AspNetCore.Mvc.Rendering;
    using Microsoft.AspNetCore.Mvc.ViewEngines;
    using Microsoft.AspNetCore.Mvc.ViewFeatures;
    using System.IO;
    using System.Threading.Tasks;
    
    namespace CC.Web.Helpers
    {
        public static class ControllerExtensions
        {
            public static async Task<string> RenderViewAsync<TModel>(this Controller controller, string viewName, TModel model, bool partial = false)
            {
                if (string.IsNullOrEmpty(viewName))
                {
                    viewName = controller.ControllerContext.ActionDescriptor.ActionName;
                }
    
                controller.ViewData.Model = model;
    
                using (var writer = new StringWriter())
                {
                    IViewEngine viewEngine = controller.HttpContext.RequestServices.GetService(typeof(ICompositeViewEngine)) as ICompositeViewEngine;
                    ViewEngineResult viewResult = viewEngine.FindView(controller.ControllerContext, viewName, !partial);
    
                    if (viewResult.Success == false)
                    {
                        return $"A view with the name {viewName} could not be found";
                    }
    
                    ViewContext viewContext = new ViewContext(
                        controller.ControllerContext,
                        viewResult.View,
                        controller.ViewData,
                        controller.TempData,
                        writer,
                        new HtmlHelperOptions()
                    );
    
                    await viewResult.View.RenderAsync(viewContext);
    
                    return writer.GetStringBuilder().ToString();
                }
            }
        }
    }
    

    然后执行:

    viewHtml = await this.RenderViewAsync("Report", model);
    

    或者这对于PartialView:

    partialViewHtml = await this.RenderViewAsync("Report", model, true);
    
  • -1

    上面的答案很好,但需要调整以使任何标记助手工作(我们需要使用实际的http上下文) . 此外,您还需要在视图中显式设置布局以获取呈现的布局 .

    public class ViewRenderService : IViewRenderService
    {
        private readonly IRazorViewEngine _razorViewEngine;
        private readonly ITempDataProvider _tempDataProvider;
        private readonly IServiceProvider _serviceProvider;
        private readonly IHostingEnvironment _env;
        private readonly HttpContext _http;
    
        public ViewRenderService(IRazorViewEngine razorViewEngine, ITempDataProvider tempDataProvider, IServiceProvider serviceProvider, IHostingEnvironment env, IHttpContextAccessor ctx)
        {
            _razorViewEngine = razorViewEngine; _tempDataProvider = tempDataProvider; _serviceProvider = serviceProvider; _env = env; _http = ctx.HttpContext;
        }
    
        public async Task<string> RenderToStringAsync(string viewName, object model)
        {
            var actionContext = new ActionContext(_http, new RouteData(), new ActionDescriptor());
    
            using (var sw = new StringWriter())
            {
                var viewResult = _razorViewEngine.FindView(actionContext, viewName, false);
                //var viewResult = _razorViewEngine.GetView(_env.WebRootPath, viewName, false); // For views outside the usual Views folder
                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(_http, _tempDataProvider), sw, new HtmlHelperOptions());
                viewContext.RouteData = _http.GetRouteData();
                await viewResult.View.RenderAsync(viewContext);
                return sw.ToString();
            }
        }
    }
    
  • 2

    我尝试了在@ Dotan Core 2.1中由@Hasan A Yousef回答的解决方案,但是csthml对我不起作用 . 它总是抛出NullReferenceException,见截图 .
    enter image description here

    为了解决这个问题,我将Html.ViewData.Model分配给一个新对象 . 这是我的代码 .

    @page
    @model InviteViewModel 
    @{
        var inviteViewModel = Html.ViewData.Model;
    }
    
    <p>
        <strong>User Id:</strong> <code>@inviteViewModel.UserId </code>
    </p>
    
  • 28

    下面的链接解决了几乎相同的问题:

    Where are the ControllerContext and ViewEngines properties in MVC 6 Controller?

    在Hasan A Yousef的回答中,我必须进行与上面链接相同的更改,以使其适用于我:

    using Microsoft.AspNetCore.Hosting;
    using Microsoft.AspNetCore.Http;
    using Microsoft.AspNetCore.Mvc;
    using Microsoft.AspNetCore.Mvc.Abstractions;
    using Microsoft.AspNetCore.Mvc.ModelBinding;
    using Microsoft.AspNetCore.Mvc.Razor;
    using Microsoft.AspNetCore.Mvc.Rendering;
    using Microsoft.AspNetCore.Mvc.ViewFeatures;
    using Microsoft.AspNetCore.Routing;
    using System;
    using System.IO;
    using System.Threading.Tasks;
    
    public class ViewRenderService : IViewRenderService
    {
        private readonly IRazorViewEngine _razorViewEngine;
        private readonly ITempDataProvider _tempDataProvider;
        private readonly IServiceProvider _serviceProvider;
        private readonly IHostingEnvironment _env;
    
        public ViewRenderService(IRazorViewEngine razorViewEngine, ITempDataProvider tempDataProvider, IServiceProvider serviceProvider, IHostingEnvironment env)
        {
            _razorViewEngine = razorViewEngine; _tempDataProvider = tempDataProvider; _serviceProvider = serviceProvider; _env = env;
        }
    
        public async Task<string> RenderToStringAsync(string viewName, object model)
        {
            var httpContext = new DefaultHttpContext { RequestServices = _serviceProvider };
            var actionContext = new ActionContext(httpContext, new RouteData(), new ActionDescriptor());
    
            using (var sw = new StringWriter()) {
                //var viewResult = _razorViewEngine.FindView(actionContext, viewName, false);
                var viewResult = _razorViewEngine.GetView(_env.WebRootPath, 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();
            }
        }
    
  • 23

    Red的答案让我99%的方式,但如果你的意见在一个意想不到的位置,它不起作用 . 这是我对此的解决方法 .

    using Microsoft.AspNetCore.Hosting;
    using Microsoft.AspNetCore.Mvc;
    using Microsoft.AspNetCore.Mvc.Rendering;
    using Microsoft.AspNetCore.Mvc.ViewEngines;
    using Microsoft.AspNetCore.Mvc.ViewFeatures;
    using System.IO;
    using System.Threading.Tasks;
    
    namespace Example
    {
        public static class ControllerExtensions
        {
            public static async Task<string> RenderViewAsync<TModel>(this Controller controller, string viewName, TModel model, bool isPartial = false)
            {
                if (string.IsNullOrEmpty(viewName))
                {
                    viewName = controller.ControllerContext.ActionDescriptor.ActionName;
                }
    
                controller.ViewData.Model = model;
    
                using (var writer = new StringWriter())
                {
                    IViewEngine viewEngine = controller.HttpContext.RequestServices.GetService(typeof(ICompositeViewEngine)) as ICompositeViewEngine;
                    ViewEngineResult viewResult = GetViewEngineResult(controller, viewName, isPartial, viewEngine);
    
                    if (viewResult.Success == false)
                    {
                        throw new System.Exception($"A view with the name {viewName} could not be found");
                    }
    
                    ViewContext viewContext = new ViewContext(
                        controller.ControllerContext,
                        viewResult.View,
                        controller.ViewData,
                        controller.TempData,
                        writer,
                        new HtmlHelperOptions()
                    );
    
                    await viewResult.View.RenderAsync(viewContext);
    
                    return writer.GetStringBuilder().ToString();
                }
            }
    
            private static ViewEngineResult GetViewEngineResult(Controller controller, string viewName, bool isPartial, IViewEngine viewEngine)
            {
                if (viewName.StartsWith("~/"))
                {
                    var hostingEnv = controller.HttpContext.RequestServices.GetService(typeof(IHostingEnvironment)) as IHostingEnvironment;
                    return viewEngine.GetView(hostingEnv.WebRootPath, viewName, !isPartial);
                }
                else
                {
                    return viewEngine.FindView(controller.ControllerContext, viewName, !isPartial);
    
                }
            }
        }
    }
    

    这允许您使用它,如下所示:

    var emailBody = await this.RenderViewAsync("~/My/Different/View.cshtml", myModel);
    

相关问题