[asp.net-mvc] ASP.NET MVC 뷰를 문자열로 렌더링하는 방법은 무엇입니까?

두 가지 다른보기 (하나는 이메일로 전송되는 문자열)와 다른 하나는 사용자에게 표시되는 페이지를 출력하고 싶습니다.

ASP.NET MVC 베타에서 가능합니까?

여러 예제를 시도했습니다.

1. ASP.NET MVC 베타에서 RenderPartial을 문자열로

이 예제를 사용하면 “HTTP 헤더를 보낸 후 리디렉션 할 수 없습니다.”라는 메시지가 나타납니다.

2. MVC 프레임 워크 : 뷰의 출력 캡처

이것을 사용하면 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);
}

Tim Scott의 답변 (나에 의해 조금 변경되고 형식화 됨) :

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



답변

여기에 내가 생각해 낸 것이 있으며 나를 위해 일하고 있습니다. 컨트롤러 기본 클래스에 다음 방법을 추가했습니다. (이러한 정적 메소드를 컨트롤러를 매개 변수로 허용하는 다른 곳에서 항상 만들 수 있습니다)

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

편집 : 면도기 코드가 추가되었습니다.


답변

이 대답은 내 길에 없습니다. 이것은 원래 https://stackoverflow.com/a/2759898/2318354 에서 가져온 것이지만 여기서는 “Static”키워드와 함께 사용하여 모든 컨트롤러에 공통으로 사용하는 방법을 보여주었습니다.

이를 위해서는 static클래스 파일에서 클래스 를 만들어야 합니다. (클래스 파일 이름은 Utils.cs라고 입력하십시오)

이 예는 For Razor입니다.

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

이제 “this”를 매개 변수로 Controller에 전달하여 다음과 같이 Controller File에 NameSpace를 추가하여 컨트롤러에서이 클래스를 호출 할 수 있습니다.

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

@Sergey가 제안한 대로이 확장 방법은 아래에 주어진 것처럼 cotroller에서 호출 할 수 있습니다

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

이것이 코드를 깨끗하고 깔끔하게 만드는 데 도움이되기를 바랍니다.


답변

이것은 나를 위해 작동합니다 :

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


답변

현재 HttpContext의 Response 스트림을 엉망으로 만들지 않고 문자열로 뷰를 렌더링하는 새로운 솔루션을 찾았습니다 (응답의 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);

//...


답변

이 문서에서는 다른 시나리오에서 뷰를 문자열로 렌더링하는 방법을 설명합니다.

  1. 다른 자체 ActionMethods를 호출하는 MVC Controller
  2. 다른 MVC 컨트롤러의 ActionMethod를 호출하는 MVC 컨트롤러
  3. MVC 컨트롤러의 ActionMethod를 호출하는 WebAPI 컨트롤러

솔루션 / 코드는 ViewRenderer 라는 클래스로 제공됩니다 . GitHub 의 Rick Stahl WestwindToolkit 의 일부입니다 .

사용법 (3.-WebAPI 예) :

string html = ViewRenderer.RenderView("~/Areas/ReportDetail/Views/ReportDetail/Index.cshtml", ReportVM.Create(id));


답변

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 Engine을 사용합니다 :
https://github.com/Antaris/RazorEngine


답변

이 방법으로 문자열로 볼 수 있습니다.

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)