tag route parameter net for data asp all c# asp.net razor asp.net-core .net-core

c# - route - Vista de retorno como cadena en.NET Core



tag helper asp net core 2 (7)

El siguiente enlace aborda casi el mismo problema:

¿Dónde están las propiedades ControllerContext y ViewEngines en MVC 6 Controller?

En la respuesta de Hasan A Yousef, tuve que hacer el mismo cambio que en el enlace de arriba para que funcione:

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(); } }

Encontré algún artículo sobre cómo devolver la vista a la cadena en ASP.NET, pero no pude encubrir ninguno para poder ejecutarlo con .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(); } }

que se supone que puede llamar desde un controlador usando:

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

Cuando intento ejecutar lo anterior en .NET Core obtengo muchos errores de compilación.

Traté de convertirlo para que funcione con .NET Core, pero fallé, ¿alguien puede ayudarme a mencionar el using .. requerido using .. y las "dependencies": { "Microsoft.AspNetCore.Mvc": "1.1.0", ... }, requeridas "dependencies": { "Microsoft.AspNetCore.Mvc": "1.1.0", ... }, para ser utilizado en el project.json .

algunos otros códigos de muestra están here y here y here

NOTA Necesito la solución para convertir la vista en string en .NET Core, independientemente del mismo código que se haya convertido, o de otra manera que pueda hacerlo.


Gracias a Paris Polyzos y su article .

Estoy volviendo a publicar su código aquí, en caso de que la publicación original se elimine por algún motivo.

Cree el Service en el archivo viewToString.cs como se muestra a continuación:

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();             }         }     } }

  1. Agregue el servicio al archivo Startup.cs , como:

    using WebApplication.Services; public void ConfigureServices(IServiceCollection services) { ...     services.AddScoped<IViewRenderService, ViewRenderService>(); }

Agregue "preserveCompilationContext": true a las buildOptions en el project.json , para que el archivo se vea así:

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

  1. Defina su model , por ejemplo:

    public class InviteViewModel { public string UserId {get; set;} public string UserName {get; set;} public string ReferralCode {get; set;} public int Credits {get; set;} }

  2. Cree su Invite.cshtml por ejemplo:

    @{ ViewData["Title"] = "Contact"; } @ViewData["Title"]. user id: @Model.UserId

  3. En el Controller :

a. Defina lo siguiente al principio:

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

si. Llame y devuelva la vista con el modelo de la siguiente manera:

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

C. El ejemplo de controlador COMPLETO, podría ser como:

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;} } }


La respuesta de Red me llevó al 99% del camino, pero no funciona si sus vistas están en una ubicación inesperada. Aquí está mi solución para eso.

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

Esto le permite usarlo de la siguiente manera:

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


Las respuestas anteriores están bien, pero deben ajustarse para que los ayudantes de etiquetas funcionen (necesitamos usar el contexto http en realidad). También deberá establecer explícitamente el diseño en la vista para obtener un diseño representado.

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(); } } }



Probé la solución que respondió @Hasan A Yousef en Dotnet Core 2.1, pero el csthml no me funciona bien. Siempre arroja una NullReferenceException, mira la captura de pantalla.

Para resolverlo, asigno el Html.ViewData.Model a un nuevo objeto. Aquí está mi código.

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


Si, como yo, tiene varios controladores que necesitan esto, como en un sitio de informes, no es realmente ideal repetir este código, e incluso inyectar o llamar a otro servicio realmente no parece correcto.

Así que hice mi propia versión de lo anterior con las siguientes diferencias:

  • modelo de escritura fuerte
  • comprobación de errores al encontrar una vista
  • capacidad de representar vistas como parciales o páginas
  • asíncrono
  • implementado como una extensión de controlador
  • no se necesita 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(); } } } }

Luego solo implemente con:

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

O esto para un PartialView:

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