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


485

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

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

2
강력하게 입력 된보기로 이것을 어떻게 사용할 수 있습니까? 즉. 모델을 페이지에 어떻게 공급할 수 있습니까?
Kjensen 2016 년

Flush가 전송하기 때문에 헤더가 전송 된 후에는 컨텐츠 유형을 설정할 수 없으므로 이것을 사용하고 나중에 JsonResult를 작성할 수 없습니다.
Arnis Lapsa

하나의 정답이 없기 때문에 나는 생각합니다. :) 나는 나에게 특정한 질문을 만들었지 만, 그것이 널리 요구되는 질문이라는 것을 알았다.
Dan Atkinson

2
제안 된 솔루션은 MVC 3에서 작동하지 않습니다.
Kasper Holdum

1
@Qua : 제안 된 솔루션은 2 년이 넘었습니다. MVC 3에서도 작동하지 않을 것입니다! 게다가, 지금이 더 좋은 방법이 있습니다.
Dan Atkinson

답변:


572

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

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

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


31
뷰를 문자열로 렌더링하는 것은 라우팅과 관련이 없기 때문에 항상 "전체 라우팅 개념과 일치하지 않습니다". 왜 작동하는 답변이 다운 투표를 받았는지 잘 모르겠습니다.
벤 레시

4
Razor 버전의 메소드 선언에서 "정적"을 제거해야 할 수도 있습니다. 그렇지 않으면 ControllerContext 등을 찾을 수 없습니다.
Mike

3
불필요한 공백에 대해 고유 한 제거 방법을 구현해야합니다. 내가 머리 꼭대기에서 생각할 수있는 가장 좋은 방법은 문자열을 XmlDocument에로드 한 다음 마지막 주석에 남겨둔 링크에 따라 XmlWriter를 사용하여 문자열에 다시 쓰는 것입니다. 정말 도움이되기를 바랍니다.
Ben Lesh

3
흠 어떻게 WebApi 컨트롤러를 사용 하여이 작업을 수행해야하는지, 제안은 감사하겠습니다
Alexander

3
안녕하세요. 모든 컨트롤러가 정적 클래스를 만들어야한다는 공통점을 만들기 위해 모든 컨트롤러에 대해 "Static"키워드와 함께 사용하려면 내부에 "this"를 매개 변수로 "ControllerContext"에 매개 변수로 사용 하여이 메소드를 넣어야합니다. 여기에서 stackoverflow.com/a/18978036/2318354 를 볼 수 있습니다 .
Dilip0165

68

이 대답은 내 길에 없습니다. 이것은 원래 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);

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


1
좋은 해결책! 한 가지, RenderRazorViewToString은 실제로는 확장 메소드입니다 (이 키워드로 컨트롤러 매개 변수를 전달하기 때문에).이 확장 메소드를 다음과 같이 호출 할 수 있습니다. this.RenderRazorViewToString ( "ViewName", model);
Sergey

@ Sergey Hmmm ... 답변을 업데이트하는 것보다 괜찮다면 그 방법으로 확인하겠습니다. 어쨌든 당신의 제안에 감사드립니다.
Dilip0165

Dilip0165, var viewResult = ViewEngines.Engines.FindPartialView (controller.ControllerContext, viewName);에서 null 참조 오류가 발생했습니다. 당신은 어떤 아이디어가 있습니까?
CB4

@ CB4 함수에 전달하는 "viewName"의 문제 일 수 있다고 생각합니다. 폴더 구조에 따라 전체 경로와 함께 "viewName"을 전달해야합니다. 이걸 확인하십시오.
Dilip0165

1
@Sergey 귀하의 제안에 감사드립니다, 나는 완전히 정확한 귀하의 제안에 따라 제 답변을 업데이트했습니다
Dilip0165

32

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

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

귀하의 의견에 감사하지만 뷰 내부 렌더링에 사용되지 않습니까? 질문을 업데이트 한 컨텍스트에서 어떻게 사용할 수 있습니까?
Dan Atkinson

죄송합니다, 작년에 첫 rc가 0 인 Silverlight에 대해 아직도 생각하고 있습니다. :) 오늘이 장면을 보여 드리겠습니다. (보기 경로의 올바른 형식을 해결하자마자)
NikolaiDante

이것은 여전히 ​​RC1에서 리디렉션을 중단합니다
패배

패배 : 아니오, 그렇지 않습니다. 그렇다면, 당신은 뭔가 잘못하고있는 것입니다.
Dan Atkinson

이것을 합병 stackoverflow.com/questions/520863/... partialview을 잡을려고했는데, ViewEnginesCollection의 추가 인식이있어 stackoverflow.com/questions/520863/...을 . : E
Arnis Lapsa

31

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

//...

HtmlHelper 객체에는 RenderPartial 메서드가 없습니다. 불가능합니다-html.RenderPartial (viewName, viewData);
MartinF

1
ASP.NET MVC 릴리스 1.0에는 몇 가지 RenderPartial 확장 메소드가 있습니다. 특히 사용중인 것은 System.Web.Mvc.Html.RenderPartialExtensions.RenderPartial (이 HtmlHelper, string, object)입니다. 이 방법이 MVC의 최신 개정판에 추가되었고 이전 버전에는 없었는지 여부를 알 수 없습니다.
LorenzCK

감사. 사용 선언에 System.Web.Mvc.Html 네임 스페이스를 추가하기 만하면됩니다 (물론 html.RenderPartial (..)에 액세스 할 수 없음 :))
MartinF

누구든지 MVC2의 RC와 함께 작동합니까? 그들은 추가 Textwriter 매개 변수를 ViewContext에 추가했습니다. 새 StringWriter ()를 추가하려고 시도했지만 작동하지 않았습니다.
beckelmw

1
@ beckelmw : 응답을 업데이트했습니다. 새 인스턴스 StringWriter를 쓰려면 사용 중인 원본 을 전달해야 합니다 StringBuilder. 그렇지 않으면 새 인스턴스가 표시되거나 뷰의 출력이 손실되지 않습니다.
LorenzCK

11

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

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

3
또한 NuGet 패키지 West Wind Web MVC 유틸리티 ( nuget.org/packages/Westwind.Web.Mvc ) 로도 제공 됩니다. 보너스로, 뷰 렌더러는 부분 뷰뿐만 아니라 레이아웃을 포함한 전체 뷰도 렌더링 할 수 있습니다. 코드가있는 블로그 기사 : weblog.west-wind.com/posts/2012/May/30/…
Jeroen K

이것이 작은 패키지로 나뉘면 좋을 것입니다. Nuget 패키지는 web.config를 많이 변경하고 js 파일을 프로젝트에 추가 한 후 제거 할 때 정리되지 않습니다 : /
Josh Noe

8

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


좋은! WebForms 구문에 대해 비슷한 구문 분석 엔진이 있는지 알고 있습니까? 아직 Razor로 아직 이동할 수없는 오래된 WebForms 뷰가 있습니다.
Dan Atkinson

안녕하세요, 나는 면도기 엔진에 많은 문제가 있었고 오류보고는 그리 좋지 않습니다. Url 도우미가 지원되지 않는다고 생각합니다
Layinka

@Layinka 이것이 도움이되는지 확실하지 않지만 대부분의 오류 정보는 CompilerErrors예외 의 속성에 있습니다.
Josh Noe

5

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

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)

4

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

3

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

2

다른 웹 사이트에서 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;

        }

    }

더에 면도기 render- MVC3보기 문자열로 렌더링


예, 이것은 실제로 수용된 답변의 사본입니다. :)
Dan Atkinson

2

ControllerContext를 전달하지 않고 서비스 계층에서 문자열로 뷰를 렌더링하려면 여기에 좋은 Rick Strahl 기사가 있습니다. 에 일반 컨트롤러를 작성하는 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;
}

그런 다음 서비스 클래스에서보기를 렌더링하십시오.

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

1

빠른 팁

강력한 형식의 Model의 경우 RenderViewToString에 전달하기 전에 ViewData.Model 속성에 추가하십시오. 예 :

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

0

더 알려지지 않은 질문을 반복하려면 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

다음은 ASP.NETCore RC2에 대해 작성한 클래스입니다. Razor를 사용하여 html 전자 메일을 생성 할 수 있도록 사용합니다.

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

위의 방법으로 오류가 발생했을 때 면도기보기 페이지를 렌더링하는 더 좋은 방법을 찾았습니다.이 양식은 웹 양식 환경과 mvc 환경 모두에 적합합니다. 컨트롤러가 필요하지 않습니다.

다음은 코드 예제입니다.이 예제에서는 비동기 http 핸들러를 사용하여 mvc 작업을 시뮬레이션했습니다.

    /// <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());
        }
    }
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.