Search code examples
asp.net-coreasp.net-core-mvcrazorengineasp.net5

How to get view file content as plain string


ASP.NET Core 5 MVC application uses razor engine from https://github.com/adoconnection/RazorEngineCore to render views as strings at runtime.

How to get view ~/Views/Checkout/Order.cshtml content as string?

I tried

ReadTemplate("~/Views/Checkout/Order.cshtml")

but it throws an error. Application may deployed as single file or as separate files.

string ReadTemplate(string filename)
{
    var stream = GetType().Assembly.GetManifestResourceStream(filename);

    if (stream == null)
        throw new ApplicationException(filename + " view not found");

    StreamReader reader = new(stream);
    string template = reader.ReadToEnd();
    reader.Close();
    return template;
}

Solution

  • How to get view ~/Views/Checkout/Order.cshtml content as string?

    You could try to create a ViewRenderService with the following code:

    public interface IViewRenderService
    {
        Task<string> RenderToString(string viewName, object model);
    }
    public class ViewRenderService : IViewRenderService
    {
        private readonly IRazorViewEngine _razorViewEngine;
        private readonly ITempDataProvider _tempDataProvider;
        private readonly IHttpContextAccessor _contextAccessor;
    
        public ViewRenderService(IRazorViewEngine razorViewEngine,
                                 ITempDataProvider tempDataProvider,
                                 IHttpContextAccessor contextAccessor)
        {
            _razorViewEngine = razorViewEngine;
            _tempDataProvider = tempDataProvider;
            _contextAccessor = contextAccessor;
        }
    
        public async Task<string> RenderToString(string viewName, object model)
        {
            var actionContext = new ActionContext(_contextAccessor.HttpContext, _contextAccessor.HttpContext.GetRouteData(), new ActionDescriptor());
    
            await using var sw = new StringWriter();
            var viewResult = FindView(actionContext, viewName);
    
            if (viewResult == 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,
                viewDictionary,
                new TempDataDictionary(actionContext.HttpContext, _tempDataProvider),
                sw,
                new HtmlHelperOptions()
            );
    
            await viewResult.RenderAsync(viewContext);
            return sw.ToString();
        }
    
        private IView FindView(ActionContext actionContext, string viewName)
        {
            var getViewResult = _razorViewEngine.GetView(executingFilePath: null, viewPath: viewName, isMainPage: true);
            if (getViewResult.Success)
            {
                return getViewResult.View;
            }
    
            var findViewResult = _razorViewEngine.FindView(actionContext, viewName, isMainPage: true);
            if (findViewResult.Success)
            {
                return findViewResult.View;
            }
    
            var searchedLocations = getViewResult.SearchedLocations.Concat(findViewResult.SearchedLocations);
            var errorMessage = string.Join(
                Environment.NewLine,
                new[] { $"Unable to find view '{viewName}'. The following locations were searched:" }.Concat(searchedLocations));
    
            throw new InvalidOperationException(errorMessage);
        }
    }
    

    Add the following references in the above service:

    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.ViewEngines;
    using Microsoft.AspNetCore.Mvc.ViewFeatures;
    using Microsoft.AspNetCore.Routing;
    

    Then, register the ViewRenderService in the Startup.ConfigureServices method:

    services.AddScoped<IViewRenderService, ViewRenderService>();
    

    After that, you could use this service to render view page as a string:

    public class HomeController : Controller
    { 
        private readonly IViewRenderService _viewRenderService;
        public HomeController(IViewRenderService viewRenderService)
        {  
            _viewRenderService = viewRenderService;
        }
         
        public async Task<IActionResult> CategoryIndex()
        {  
           //The model will be transferred to the View page.
            var stulist = new List<Student>() { new Student() { StudentName = "AA" }, new Student() { StudentName = "BB" } };
    
            var result =  _viewRenderService.RenderToString("Views/Student/Index.cshtml", stulist).Result;
    
            return View();
        }
    

    The result like this:

    enter image description here