在 ASP.NET Core 2.2 中将局部视图渲染为 HTML 字符串

Jac*_*ack 5 c# asp.net asp.net-core-mvc asp.net-core

我正在尝试使用 AJAX 来调用我的控制器并返回一个带有模型作为字符串的局部视图,以便我可以将它注入到我的 HTML 中。我之前在 MVC5 中使用控制器接口完成了此操作,但我似乎无法找到有关如何为 Asp.Net Core 2.2 中的部分视图执行此操作的任何信息。我找到了如何将视图呈现为字符串的示例,但我无法修改它们以适用于局部视图。

控制器动作:

    public JsonResult GetUpsertPartialView(MessageBoard messageBoard)
    {
        Dictionary<string, string> result = new Dictionary<string, string>();
        string errorState = "0";
        string errorMessage = "";

        try
        {
            result["view"] = ""; // My call to render the Partial View would go here.
        }
        catch (Exception)
        {
            errorState = "1";
            errorMessage = "An error was encountered while constructing the View.";
        }
        result["errorState"] = errorState;
        result["errorMessage"] = errorMessage;
        return Json(result);
    }
Run Code Online (Sandbox Code Playgroud)

AJAX 调用:

   $.ajax({
      type: "GET",
      url: "../Home/GetUpsertPartialView/",
      data: messageBoardData,
      contentType: 'application/json; charset=utf-8',
      success: function (data) {
         console.log(data);
         $("#messageBoardModalBody").val(data.view);
         $("#messageBoardModal").modal("show");
      }
   });
Run Code Online (Sandbox Code Playgroud)

我已经确认我的控制器正在被命中,参数被正确传递,并且我正在从 Action 接收到正确的数据返回到我的 AJAX 调用。我唯一缺少的是将局部视图直接渲染为字符串的能力。

如果在 Asp.net Core 中有另一种方法可以做到这一点,我愿意接受其他选择。

注意:我这样做只是为了学习 Asp.Net Core。如果需要更多信息,请告诉我。

Moh*_*mad 7

ControllerExtensions 实现

    public static class ControllerExtensions
{
    /// <summary>
    /// Render a partial view to string.
    /// </summary>
    public static async Task<string> RenderViewToStringAsync(this Controller controller, string viewNamePath, object model = null)
    {
        if (string.IsNullOrEmpty(viewNamePath))
            viewNamePath = controller.ControllerContext.ActionDescriptor.ActionName;

        controller.ViewData.Model = model;

        using (StringWriter writer = new StringWriter())
        {
            try
            {
                var view = FindView(controller, viewNamePath);

                ViewContext viewContext = new ViewContext(
                    controller.ControllerContext,
                    view,
                    controller.ViewData,
                    controller.TempData,
                    writer,
                    new HtmlHelperOptions()
                );

                await view.RenderAsync(viewContext);

                return writer.GetStringBuilder().ToString();
            }
            catch (Exception exc)
            {
                return $"Failed - {exc.Message}";
            }
        }
    }

    private static IView FindView(Controller controller, string viewNamePath)
    {
        IViewEngine viewEngine = controller.HttpContext.RequestServices.GetService(typeof(ICompositeViewEngine)) as ICompositeViewEngine;

        ViewEngineResult viewResult = null;

        if (viewNamePath.EndsWith(".cshtml"))
            viewResult = viewEngine.GetView(viewNamePath, viewNamePath, false);
        else
            viewResult = viewEngine.FindView(controller.ControllerContext, viewNamePath, false);

        if (!viewResult.Success)
        {
            var endPointDisplay = controller.HttpContext.GetEndpoint().DisplayName;

            if (endPointDisplay.Contains(".Areas."))
            {
                //search in Areas
                var areaName = endPointDisplay.Substring(endPointDisplay.IndexOf(".Areas.") + ".Areas.".Length);
                areaName = areaName.Substring(0, areaName.IndexOf(".Controllers."));

                viewNamePath = $"~/Areas/{areaName}/views/{controller.HttpContext.Request.RouteValues["controller"]}/{controller.HttpContext.Request.RouteValues["action"]}.cshtml";

                viewResult = viewEngine.GetView(viewNamePath, viewNamePath, false);
            }

            if (!viewResult.Success)
                throw new Exception($"A view with the name '{viewNamePath}' could not be found");

        }

        return viewResult.View;
    }

}
Run Code Online (Sandbox Code Playgroud)

在控制器操作中的用法:

var html = await this.RenderViewToStringAsync("actionName" , model);
Run Code Online (Sandbox Code Playgroud)


Aha*_*yon 5

可以使用服务从任何位置(控制器、页面模型等)获取部分视图(或多个):

这个答案的起源,他值得称赞(我在这里添加它是因为我需要类似的东西,并且我花了很长时间才找到它)

定义依赖注入的接口

public interface IViewRenderService
{
    Task<string> RenderToStringAsync(string viewName, object model);
}
Run Code Online (Sandbox Code Playgroud)

服务的实施

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();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

在函数中添加类ConfigureServices

services.AddScoped<IViewRenderService, ViewRenderService>();
Run Code Online (Sandbox Code Playgroud)

最后,使用该服务

string html = await m_RenderService.RenderToStringAsync("<NameOfPartial>", new Model());
Run Code Online (Sandbox Code Playgroud)

  • 我非常担心这个(或变体)确实是唯一的解决方案。获取单个 Partial 的字符串表示形式非常麻烦。 (2认同)

Joe*_*tte 3

为什么 Json 结果带有 html 字符串?可以直接返回一个partial view来返回html。

public IActionResult GetUpsertPartialView(MessageBoard messageBoard)
{

    return PartialView("someviewname", messageBoard);
}
Run Code Online (Sandbox Code Playgroud)

  • 大多数时候,除了部分视图之外,我还需要返回其他数据。具体来说,我需要在大多数情况下能够返回错误数据。这就是为什么我想将它作为 Json 结果的一部分返回。 (3认同)