Как визуализировать представление ASP.NET MVC в виде строки?

Я хочу вывести два разных представления (одно в виде строки, которая будет отправлена ​​по электронной почте), а другое - на страницу, отображаемую пользователю.

Возможно ли это в ASP.NET MVC beta?

Я пробовал несколько примеров:

1. RenderPartial к String в ASP.NET MVC Beta

Если я использую этот пример, я получаю сообщение «Не удается перенаправить после отправки заголовков HTTP».

2. MVC Framework: получение выходных данных представления

Если я использую это, мне кажется, что я не могу выполнить redirectToAction, так как он пытается отобразить представление, которое может не существовать. Если я верну вид, он полностью испорчен и выглядит не совсем правильно.

У кого-нибудь есть какие-либо идеи / решения этих проблем, которые у меня есть, или есть предложения для лучших?

Большое спасибо!

Ниже приведен пример. Я пытаюсь создать метод GetViewForEmail :

public ActionResult OrderResult(string ref)
{
    //Get the order
    Order order = OrderService.GetOrder(ref);

    //The email helper would do the meat and veg by getting the view as a string
    //Pass the control name (OrderResultEmail) and the model (order)
    string emailView = GetViewForEmail("OrderResultEmail", order);

    //Email the order out
    EmailHelper(order, emailView);
    return View("OrderResult", order);
}

Принял ответ от Тима Скотта (немного измененный и отформатированный мной):

public virtual string RenderViewToString(
    ControllerContext controllerContext,
    string viewPath,
    string masterPath,
    ViewDataDictionary viewData,
    TempDataDictionary tempData)
{
    Stream filter = null;
    ViewPage viewPage = new ViewPage();

    //Right, create our view
    viewPage.ViewContext = new ViewContext(controllerContext, new WebFormView(viewPath, masterPath), viewData, tempData);

    //Get the response context, flush it and get the response filter.
    var response = viewPage.ViewContext.HttpContext.Response;
    response.Flush();
    var oldFilter = response.Filter;

    try
    {
        //Put a new filter into the response
        filter = new MemoryStream();
        response.Filter = filter;

        //Now render the view into the memorystream and flush the response
        viewPage.ViewContext.View.Render(viewPage.ViewContext, viewPage.ViewContext.HttpContext.Response.Output);
        response.Flush();

        //Now read the rendered view.
        filter.Position = 0;
        var reader = new StreamReader(filter, response.ContentEncoding);
        return reader.ReadToEnd();
    }
    finally
    {
        //Clean up.
        if (filter != null)
        {
            filter.Dispose();
        }

        //Now replace the response filter
        response.Filter = oldFilter;
    }
}

Пример использования

Принимая звонок от контроллера, чтобы получить подтверждение заказа по электронной почте, передав местоположение Site.Master.

string myString = RenderViewToString(this.ControllerContext, "~/Views/Order/OrderResultEmail.aspx", "~/Views/Shared/Site.Master", this.ViewData, this.TempData);
27.01.2009 11:43:56
Как вы можете использовать это с видом, который строго типизирован? То есть. как я могу подать модель на страницу?
Kjensen 11.06.2009 12:07:57
Невозможно использовать это и создать JsonResult впоследствии, потому что тип содержимого не может быть установлен после отправки заголовков (потому что Flush отправляет их).
Arnis Lapsa 17.11.2009 13:16:06
Потому что нет единого правильного ответа, я полагаю. :) Я создал вопрос, который был специфичен для меня, но я знал, что он также будет широко задаваться.
Dan Atkinson 21.07.2010 08:25:47
Предлагаемое решение не работает в MVC 3.
Kasper Holdum 13.05.2011 12:04:53
@Qua: предлагаемому решению более двух лет. Я не ожидал бы, что это будет работать и для MVC 3! Кроме того, есть лучшие способы сделать это сейчас.
Dan Atkinson 13.05.2011 12:32:35
15 ОТВЕТОВ
РЕШЕНИЕ

Вот то, что я придумал, и это работает для меня. Я добавил следующие методы в базовый класс контроллера. (Вы всегда можете сделать эти статические методы где-нибудь еще, которые принимают контроллер в качестве параметра, я полагаю)

Стиль MVC2 .ascx

protected string RenderViewToString<T>(string viewPath, T model) {
  ViewData.Model = model;
  using (var writer = new StringWriter()) {
    var view = new WebFormView(ControllerContext, viewPath);
    var vdd = new ViewDataDictionary<T>(model);
    var viewCxt = new ViewContext(ControllerContext, view, vdd,
                                new TempDataDictionary(), writer);
    viewCxt.View.Render(viewCxt, writer);
    return writer.ToString();
  }
}

Бритва .cshtml стиль

public string RenderRazorViewToString(string viewName, object model)
{
  ViewData.Model = model;
  using (var sw = new StringWriter())
  {
    var viewResult = ViewEngines.Engines.FindPartialView(ControllerContext,
                                                             viewName);
    var viewContext = new ViewContext(ControllerContext, viewResult.View,
                                 ViewData, TempData, sw);
    viewResult.View.Render(viewContext, sw);
    viewResult.ViewEngine.ReleaseView(ControllerContext, viewResult.View);
    return sw.GetStringBuilder().ToString();
  }
}

Редактировать: добавлен код бритвы.

569
12.05.2015 12:38:19
Визуализация представления строки всегда «несовместима со всей концепцией маршрутизации», поскольку она не имеет ничего общего с маршрутизацией. Я не уверен, почему ответ, который работает, получил отрицательный голос.
Ben Lesh 3.11.2010 20:15:03
Я думаю, что вам может потребоваться удалить «статический» из объявления метода версии Razor, в противном случае он не может найти ControllerContext и др.
Mike 14.01.2012 05:10:56
Вам нужно будет реализовать свой собственный метод удаления для этих лишних пробелов. Лучший способ, который я могу придумать, - загрузить строку в XmlDocument, а затем записать ее обратно в строку с помощью XmlWriter по ссылке, которую я оставил в своем последнем комментарии. Я действительно надеюсь, что это поможет.
Ben Lesh 16.02.2012 14:01:10
Хм, как я должен сделать это с помощью контроллера WebApi, любые предложения будут оценены
Alexander 6.05.2013 16:41:47
Всем привет, используйте его с ключевым словом «Static» для всех контроллеров, чтобы сделать его общим, вы должны сделать статический класс, и внутри него вы должны поместить этот метод с «this» в качестве параметра в «ControllerContext». Вы можете увидеть здесь stackoverflow.com/a/18978036/2318354 это.
Dilip0165 24.09.2013 09:49:26

Это работает для меня:

public virtual string RenderView(ViewContext viewContext)
{
    var response = viewContext.HttpContext.Response;
    response.Flush();
    var oldFilter = response.Filter;
    Stream filter = null;
    try
    {
        filter = new MemoryStream();
        response.Filter = filter;
        viewContext.View.Render(viewContext, viewContext.HttpContext.Response.Output);
        response.Flush();
        filter.Position = 0;
        var reader = new StreamReader(filter, response.ContentEncoding);
        return reader.ReadToEnd();
    }
    finally
    {
        if (filter != null)
        {
            filter.Dispose();
        }
        response.Filter = oldFilter;
    }
}
32
27.01.2009 19:48:12
Спасибо за ваш комментарий, но разве это не используется для рендеринга внутри представления? Как я мог использовать это в контексте, с которым я обновил вопрос?
Dan Atkinson 27.01.2009 21:44:35
Извините, я все еще думаю о Silverlight в прошлом году, у которого первый rc был 0. :) Сегодня я попробую. (Как только я
NikolaiDante 30.01.2009 08:57:06
Это все еще ломает перенаправления в RC1
defeated 7.02.2009 08:20:49
победил: нет, это не так. Если это так, то вы делаете что-то не так.
Dan Atkinson 18.02.2009 20:28:48
Объединил это со stackoverflow.com/questions/520863/… , добавил осведомленность о ViewEnginesCollection, попытался открыть частичное представление и получил этот stackoverflow.com/questions/520863/… . : E
Arnis Lapsa 17.11.2009 11:26:27

Быстрая подсказка

Для строго типизированной модели просто добавьте ее в свойство ViewData.Model, прежде чем переходить к RenderViewToString. например

this.ViewData.Model = new OrderResultEmailViewModel(order);
string myString = RenderViewToString(this.ControllerContext, "~/Views/Order/OrderResultEmail.aspx", "~/Views/Shared/Site.Master", this.ViewData, this.TempData);
1
15.06.2009 16:42:27

Я использую MVC 1.0 RTM, и ни одно из вышеперечисленных решений не помогло мне. Но этот сделал:

Public Function RenderView(ByVal viewContext As ViewContext) As String

    Dim html As String = ""

    Dim response As HttpResponse = HttpContext.Current.Response

    Using tempWriter As New System.IO.StringWriter()

        Dim privateMethod As MethodInfo = response.GetType().GetMethod("SwitchWriter", BindingFlags.NonPublic Or BindingFlags.Instance)

        Dim currentWriter As Object = privateMethod.Invoke(response, BindingFlags.NonPublic Or BindingFlags.Instance Or BindingFlags.InvokeMethod, Nothing, New Object() {tempWriter}, Nothing)

        Try
            viewContext.View.Render(viewContext, Nothing)
            html = tempWriter.ToString()
        Finally
            privateMethod.Invoke(response, BindingFlags.NonPublic Or BindingFlags.Instance Or BindingFlags.InvokeMethod, Nothing, New Object() {currentWriter}, Nothing)
        End Try

    End Using

    Return html

End Function
3
13.07.2009 20:44:07

Я нашел новое решение, которое отображает строку без необходимости связываться с потоком Response текущего HttpContext (который не позволяет вам изменять ContentType ответа или другие заголовки).

По сути, все, что вам нужно сделать, это создать поддельный HttpContext для представления, которое будет отображаться само по себе:

/// <summary>Renders a view to string.</summary>
public static string RenderViewToString(this Controller controller,
                                        string viewName, object viewData) {
    //Create memory writer
    var sb = new StringBuilder();
    var memWriter = new StringWriter(sb);

    //Create fake http context to render the view
    var fakeResponse = new HttpResponse(memWriter);
    var fakeContext = new HttpContext(HttpContext.Current.Request, fakeResponse);
    var fakeControllerContext = new ControllerContext(
        new HttpContextWrapper(fakeContext),
        controller.ControllerContext.RouteData,
        controller.ControllerContext.Controller);

    var oldContext = HttpContext.Current;
    HttpContext.Current = fakeContext;

    //Use HtmlHelper to render partial view to fake context
    var html = new HtmlHelper(new ViewContext(fakeControllerContext,
        new FakeView(), new ViewDataDictionary(), new TempDataDictionary()),
        new ViewPage());
    html.RenderPartial(viewName, viewData);

    //Restore context
    HttpContext.Current = oldContext;    

    //Flush memory and return output
    memWriter.Flush();
    return sb.ToString();
}

/// <summary>Fake IView implementation used to instantiate an HtmlHelper.</summary>
public class FakeView : IView {
    #region IView Members

    public void Render(ViewContext viewContext, System.IO.TextWriter writer) {
        throw new NotImplementedException();
    }

    #endregion
}

Это работает в ASP.NET MVC 1.0 вместе с ContentResult, JsonResult и т. Д. (Изменение заголовков исходного HttpResponse не приводит к исключению « Сервер не может установить тип содержимого после отправки заголовков HTTP »).

Обновление: в ASP.NET MVC 2.0 RC код немного меняется, потому что мы должны передать StringWriterиспользуемый для записи представления в ViewContext:

//...

//Use HtmlHelper to render partial view to fake context
var html = new HtmlHelper(
    new ViewContext(fakeControllerContext, new FakeView(),
        new ViewDataDictionary(), new TempDataDictionary(), memWriter),
    new ViewPage());
html.RenderPartial(viewName, viewData);

//...
30
30.11.2015 22:58:04
В объекте HtmlHelper нет метода RenderPartial. Это невозможно - html.RenderPartial (viewName, viewData);
MartinF 8.08.2009 12:53:12
В версии 1.0 ASP.NET MVC есть несколько методов расширения RenderPartial. В частности, я использую System.Web.Mvc.Html.RenderPartialExtensions.RenderPartial (это HtmlHelper, string, object). Я не знаю, был ли метод добавлен в последних ревизиях MVC и не присутствовал в более ранних.
LorenzCK 8.08.2009 17:42:41
Спасибо. Просто нужно добавить пространство имен System.Web.Mvc.Html в объявление использования (иначе html.RenderPartial (..), конечно, не будет доступен :))
MartinF 9.08.2009 11:28:37
У кого-нибудь есть такая работа с RC от MVC2? Они добавили дополнительный параметр Textwriter в ViewContext. Я попытался просто добавить новый StringWriter (), но это не сработало.
beckelmw 20.01.2010 14:48:11
@beckelmw: я обновил ответ. Вы должны передать оригинал, который StringWriterвы используете для записи StringBuilder, а не новый экземпляр, иначе выходные данные представления будут потеряны.
LorenzCK 20.01.2010 20:38:15

Чтобы повторить более неизвестный вопрос, взгляните на MvcIntegrationTestFramework .

Это избавляет вас от написания собственных помощников для потоковой передачи результатов и доказывает, что работает достаточно хорошо. Я предполагаю, что это будет в тестовом проекте, и в качестве бонуса у вас будут другие возможности тестирования, как только вы получите эту настройку. Главным беспокойством, вероятно, будет сортировка цепочки зависимостей.

 private static readonly string mvcAppPath = 
     Path.GetFullPath(AppDomain.CurrentDomain.BaseDirectory 
     + "\\..\\..\\..\\MyMvcApplication");
 private readonly AppHost appHost = new AppHost(mvcAppPath);

    [Test]
    public void Root_Url_Renders_Index_View()
    {
        appHost.SimulateBrowsingSession(browsingSession => {
            RequestResult result = browsingSession.ProcessRequest("");
            Assert.IsTrue(result.ResponseText.Contains("<!DOCTYPE html"));
        });
}
0
31.03.2010 15:58:05

Я видел реализацию MVC 3 и Razor с другого сайта, у меня это сработало:

    public static string RazorRender(Controller context, string DefaultAction)
    {
        string Cache = string.Empty;
        System.Text.StringBuilder sb = new System.Text.StringBuilder();
        System.IO.TextWriter tw = new System.IO.StringWriter(sb); 

        RazorView view_ = new RazorView(context.ControllerContext, DefaultAction, null, false, null);
        view_.Render(new ViewContext(context.ControllerContext, view_, new ViewDataDictionary(), new TempDataDictionary(), tw), tw);

        Cache = sb.ToString(); 

        return Cache;

    } 

    public static string RenderRazorViewToString(string viewName, object model)
    {

        ViewData.Model = model;
        using (var sw = new StringWriter())
        {
            var viewResult = ViewEngines.Engines.FindPartialView(ControllerContext, viewName);
            var viewContext = new ViewContext(ControllerContext, viewResult.View, ViewData, TempData, sw);
            viewResult.View.Render(viewContext, sw);
            return sw.GetStringBuilder().ToString();
        }
    } 

    public static class HtmlHelperExtensions
    {
        public static string RenderPartialToString(ControllerContext context, string partialViewName, ViewDataDictionary viewData, TempDataDictionary tempData)
        {
            ViewEngineResult result = ViewEngines.Engines.FindPartialView(context, partialViewName);

            if (result.View != null)
            {
                StringBuilder sb = new StringBuilder();
                using (StringWriter sw = new StringWriter(sb))
                {
                    using (HtmlTextWriter output = new HtmlTextWriter(sw))
                    {
                        ViewContext viewContext = new ViewContext(context, result.View, viewData, tempData, output);
                        result.View.Render(viewContext, output);
                    }
                }
                return sb.ToString();
            } 

            return String.Empty;

        }

    }

Подробнее о Razor render - MVC3 View Render to String

2
20.02.2012 21:34:56
Да, это на самом деле более или менее копия принятого ответа. :)
Dan Atkinson 22.05.2012 09:52:51

Если вы хотите полностью отказаться от MVC, избегая тем самым всего беспорядка HttpContext ...

using RazorEngine;
using RazorEngine.Templating; // For extension methods.

string razorText = System.IO.File.ReadAllText(razorTemplateFileLocation);
string emailBody = Engine.Razor.RunCompile(razorText, "templateKey", typeof(Model), model);

Здесь используется удивительный движок Razor с открытым исходным кодом: https://github.com/Antaris/RazorEngine

8
21.07.2016 14:16:29
Ницца! Знаете ли вы, есть ли подобный механизм синтаксического анализа для синтаксиса WebForms? У меня все еще есть старые представления WebForms, которые пока нельзя перенести в Razor.
Dan Atkinson 19.09.2013 07:48:49
Привет, у меня было много проблем с razorengine, и сообщение об ошибке не очень приятно. Я не думаю, что Url
Layinka 15.02.2016 08:10:21
@Layinka Не уверен, поможет ли это, но большая часть информации об ошибках находится в CompilerErrorsсвойстве исключения.
Josh Noe 12.09.2017 17:31:51

Этот ответ не на моем пути. Это изначально из https://stackoverflow.com/a/2759898/2318354, но здесь я покажу, как использовать его со «статическим» ключевым словом, чтобы сделать его общим для всех контроллеров.

Для этого вы должны сделать staticкласс в файле класса. (Предположим, ваше имя файла класса Utils.cs)

Этот пример для бритвы.

Utils.cs

public static class RazorViewToString
{
    public static string RenderRazorViewToString(this Controller controller, string viewName, object model)
    {
        controller.ViewData.Model = model;
        using (var sw = new StringWriter())
        {
            var viewResult = ViewEngines.Engines.FindPartialView(controller.ControllerContext, viewName);
            var viewContext = new ViewContext(controller.ControllerContext, viewResult.View, controller.ViewData, controller.TempData, sw);
            viewResult.View.Render(viewContext, sw);
            viewResult.ViewEngine.ReleaseView(controller.ControllerContext, viewResult.View);
            return sw.GetStringBuilder().ToString();
        }
    }
}

Теперь вы можете вызвать этот класс из вашего контроллера, добавив NameSpace в ваш файл контроллера следующим образом, передав «this» в качестве параметра в Controller.

string result = RazorViewToString.RenderRazorViewToString(this ,"ViewName", model);

По предложению @Sergey этот метод расширения может также вызываться из cotroller, как указано ниже

string result = this.RenderRazorViewToString("ViewName", model);

Надеюсь, это поможет вам сделать код чистым и аккуратным.

68
21.05.2018 06:39:10
Отличное решение! Во-первых, RenderRazorViewToString на самом деле является методом расширения (поскольку вы передаете параметр контроллера с этим ключевым словом), поэтому этот метод расширения можно назвать следующим образом: this.RenderRazorViewToString ("ViewName", model);
Sergey 20.03.2016 18:11:22
@ Сергей Хммм ... Позвольте мне проверить таким образом, если это хорошо, чем я обновлю свой ответ. В любом случае, спасибо за ваше предложение.
Dilip0165 29.03.2016 08:57:04
Dilip0165, я получил ошибку нулевой ссылки на var viewResult = ViewEngines.Engines.FindPartialView (controller.ControllerContext, viewName) ;. Есть ли у вас какие-либо идеи?
CB4 10.11.2016 18:50:21
@ CB4 Я думаю, это может быть проблема "viewName", которую вы передаете в функцию. Вы должны передать «viewName» с полным путем согласно структуре вашей папки. Так что проверь это.
Dilip0165 11.11.2016 06:37:15
@ Сергей Спасибо за ваше предложение, я обновил свой ответ в соответствии с вашим предложением, который является абсолютно правильным
Dilip0165 21.05.2018 06:40:38

В этой статье описывается, как визуализировать представление в строку в различных сценариях:

  1. MVC Controller вызывает другой собственный ActionMethods
  2. MVC Controller вызывает ActionMethod другого MVC Controller
  3. Контроллер WebAPI вызывает метод ActionMet для контроллера MVC

Решение / код предоставляется в виде класса с именем ViewRenderer . Он является частью WestwindToolkit Рика Штала в GitHub .

Использование (3. - пример WebAPI):

string html = ViewRenderer.RenderView("~/Areas/ReportDetail/Views/ReportDetail/Index.cshtml", ReportVM.Create(id));
11
2.10.2014 07:13:55
Также как пакет NuGet West Wind Web MVC Utilities ( nuget.org/packages/Westwind.Web.Mvc ). В качестве бонуса средство визуализации представлений может отображать не только частичные представления, но и весь вид, включая макет. Статья в блоге с кодом: weblog.west-wind.com/posts/2012/May/30/…
Jeroen K 6.08.2015 15:01:33
Было бы здорово, если бы это было разбито на более мелкие пакеты. Пакет Nuget вносит кучу изменений в ваш web.config и добавляет в ваш проект js-файлы, которые затем не удаляются при его удалении: /
Josh Noe 8.09.2017 20:09:09

Вы получаете представление в строке, используя этот способ

protected string RenderPartialViewToString(string viewName, object model)
{
    if (string.IsNullOrEmpty(viewName))
        viewName = ControllerContext.RouteData.GetRequiredString("action");

    if (model != null)
        ViewData.Model = model;

    using (StringWriter sw = new StringWriter())
    {
        ViewEngineResult viewResult = ViewEngines.Engines.FindPartialView(ControllerContext, viewName);
        ViewContext viewContext = new ViewContext(ControllerContext, viewResult.View, ViewData, TempData, sw);
        viewResult.View.Render(viewContext, sw);

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

Мы называем этот метод двумя способами

string strView = RenderPartialViewToString("~/Views/Shared/_Header.cshtml", null)

ИЛИ

var model = new Person()
string strView = RenderPartialViewToString("~/Views/Shared/_Header.cshtml", model)
5
5.02.2018 09:25:06

Вот класс, который я написал, чтобы сделать это для ASP.NETCore RC2. Я использую его, чтобы я мог генерировать html электронную почту, используя Razor.

using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Abstractions;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.AspNetCore.Mvc.ViewEngines;
using Microsoft.AspNetCore.Mvc.ViewFeatures;
using Microsoft.AspNetCore.Routing;
using System.IO;
using System.Threading.Tasks;

namespace cloudscribe.Web.Common.Razor
{
    /// <summary>
    /// the goal of this class is to provide an easy way to produce an html string using 
    /// Razor templates and models, for use in generating html email.
    /// </summary>
    public class ViewRenderer
    {
        public ViewRenderer(
            ICompositeViewEngine viewEngine,
            ITempDataProvider tempDataProvider,
            IHttpContextAccessor contextAccesor)
        {
            this.viewEngine = viewEngine;
            this.tempDataProvider = tempDataProvider;
            this.contextAccesor = contextAccesor;
        }

        private ICompositeViewEngine viewEngine;
        private ITempDataProvider tempDataProvider;
        private IHttpContextAccessor contextAccesor;

        public async Task<string> RenderViewAsString<TModel>(string viewName, TModel model)
        {

            var viewData = new ViewDataDictionary<TModel>(
                        metadataProvider: new EmptyModelMetadataProvider(),
                        modelState: new ModelStateDictionary())
            {
                Model = model
            };

            var actionContext = new ActionContext(contextAccesor.HttpContext, new RouteData(), new ActionDescriptor());
            var tempData = new TempDataDictionary(contextAccesor.HttpContext, tempDataProvider);

            using (StringWriter output = new StringWriter())
            {

                ViewEngineResult viewResult = viewEngine.FindView(actionContext, viewName, true);

                ViewContext viewContext = new ViewContext(
                    actionContext,
                    viewResult.View,
                    viewData,
                    tempData,
                    output,
                    new HtmlHelperOptions()
                );

                await viewResult.View.RenderAsync(viewContext);

                return output.GetStringBuilder().ToString();
            }
        }
    }
}
0
9.06.2016 11:28:41

Я нашел лучший способ визуализации страницы просмотра бритвой, когда у меня возникли ошибки с помощью описанных выше методов, это решение как для среды веб-форм, так и для среды MVC. Контроллер не нужен.

Вот пример кода, в этом примере я смоделировал действие mvc с помощью асинхронного http-обработчика:

    /// <summary>
    /// Enables processing of HTTP Web requests asynchronously by a custom HttpHandler that implements the IHttpHandler interface.
    /// </summary>
    /// <param name="context">An HttpContext object that provides references to the intrinsic server objects.</param>
    /// <returns>The task to complete the http request.</returns>
    protected override async Task ProcessRequestAsync(HttpContext context)
    {
        if (this._view == null)
        {
            this.OnError(context, new FileNotFoundException("Can not find the mvc view file.".Localize()));
            return;
        }
        object model = await this.LoadModelAsync(context);
        WebPageBase page = WebPageBase.CreateInstanceFromVirtualPath(this._view.VirtualPath);
        using (StringWriter sw = new StringWriter())
        {
            page.ExecutePageHierarchy(new WebPageContext(new HttpContextWrapper(context), page, model), sw);
            await context.Response.Output.WriteAsync(sw.GetStringBuilder().ToString());
        }
    }
0
12.08.2016 16:33:38

Чтобы визуализировать представление строки в слое обслуживания без необходимости передавать ControllerContext, есть хорошая статья Рика Стрэла здесь http://www.codemag.com/Article/1312081, которая создает универсальный контроллер. Краткое содержание кода ниже:

// Some Static Class
public static string RenderViewToString(ControllerContext context, string viewPath, object model = null, bool partial = false)
{
    // first find the ViewEngine for this view
    ViewEngineResult viewEngineResult = null;
    if (partial)
        viewEngineResult = ViewEngines.Engines.FindPartialView(context, viewPath);
    else
        viewEngineResult = ViewEngines.Engines.FindView(context, viewPath, null);

    if (viewEngineResult == null)
        throw new FileNotFoundException("View cannot be found.");

    // get the view and attach the model to view data
    var view = viewEngineResult.View;
    context.Controller.ViewData.Model = model;

    string result = null;

    using (var sw = new StringWriter())
    {
        var ctx = new ViewContext(context, view, context.Controller.ViewData, context.Controller.TempData, sw);
        view.Render(ctx, sw);
        result = sw.ToString();
    }

    return result;
}

// In the Service Class
public class GenericController : Controller
{ }

public static T CreateController<T>(RouteData routeData = null) where T : Controller, new()
{
    // create a disconnected controller instance
    T controller = new T();

    // get context wrapper from HttpContext if available
    HttpContextBase wrapper;
    if (System.Web.HttpContext.Current != null)
        wrapper = new HttpContextWrapper(System.Web.HttpContext.Current);
    else
        throw new InvalidOperationException("Cannot create Controller Context if no active HttpContext instance is available.");

    if (routeData == null)
        routeData = new RouteData();

    // add the controller routing if not existing
    if (!routeData.Values.ContainsKey("controller") &&
        !routeData.Values.ContainsKey("Controller"))
        routeData.Values.Add("controller", controller.GetType().Name.ToLower().Replace("controller", ""));

    controller.ControllerContext = new ControllerContext(wrapper, routeData, controller);
    return controller;
}

Затем для рендеринга View в классе Service:

var stringView = RenderViewToString(CreateController<GenericController>().ControllerContext, "~/Path/To/View/Location/_viewName.cshtml", theViewModel, true);
2
2.03.2017 15:32:05

Дополнительный совет для ASP NET CORE:

Интерфейс:

public interface IViewRenderer
{
  Task<string> RenderAsync<TModel>(Controller controller, string name, TModel model);
}

Реализация:

public class ViewRenderer : IViewRenderer
{
  private readonly IRazorViewEngine viewEngine;

  public ViewRenderer(IRazorViewEngine viewEngine) => this.viewEngine = viewEngine;

  public async Task<string> RenderAsync<TModel>(Controller controller, string name, TModel model)
  {
    ViewEngineResult viewEngineResult = this.viewEngine.FindView(controller.ControllerContext, name, false);

    if (!viewEngineResult.Success)
    {
      throw new InvalidOperationException(string.Format("Could not find view: {0}", name));
    }

    IView view = viewEngineResult.View;
    controller.ViewData.Model = model;

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

       await view.RenderAsync(viewContext);

       return writer.ToString();
  }
}

Регистрация в Startup.cs

...
 services.AddSingleton<IViewRenderer, ViewRenderer>();
...

И использование в контроллере:

public MyController: Controller
{
  private readonly IViewRenderer renderer;
  public MyController(IViewRendere renderer) => this.renderer = renderer;
  public async Task<IActionResult> MyViewTest
  {
    var view = await this.renderer.RenderAsync(this, "MyView", model);
    return new OkObjectResult(view);
  }
}
2
28.09.2019 08:19:18