วิธีการแสดงมุมมอง ASP.NET MVC เป็นสตริง?


485

ฉันต้องการแสดงผลสองมุมมองที่แตกต่างกัน (อันหนึ่งเป็นสตริงที่จะถูกส่งเป็นอีเมล) และอีกหน้าที่แสดงให้ผู้ใช้เห็น

เป็นไปได้ใน ASP.NET MVC เบต้าหรือไม่

ฉันลองหลายตัวอย่าง:

1. RenderPartial เป็น String ใน ASP.NET MVC Beta

หากฉันใช้ตัวอย่างนี้ฉันจะได้รับ "ไม่สามารถเปลี่ยนเส้นทางหลังจากส่งส่วนหัว HTTP"

2. MVC Framework: การจับภาพเอาต์พุตของมุมมอง

หากฉันใช้สิ่งนี้ฉันดูเหมือนจะไม่สามารถทำการเปลี่ยนเส้นทางไปยังการกระทำได้เนื่องจากพยายามแสดงมุมมองที่อาจไม่มีอยู่ ถ้าฉันกลับมาดูมันจะเลอะเทอะและไม่ได้ดูเลย

ไม่มีใครมีความคิด / แนวทางแก้ไขสำหรับปัญหาเหล่านี้ที่ฉันมีหรือมีคำแนะนำใด ๆ

ขอบคุณมาก!

ด้านล่างเป็นตัวอย่าง สิ่งที่ฉันพยายามทำคือสร้างเมธอด 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

ไม่สามารถใช้สิ่งนี้และสร้าง JsonResult ในภายหลังเนื่องจากไม่สามารถตั้งค่าประเภทเนื้อหาหลังจากที่มีการส่งส่วนหัว (เนื่องจาก Flush ส่งพวกเขา)
Arnis Lapsa

เพราะไม่มีคำตอบเดียวที่ถูกต้องฉันคิดว่า :) ฉันสร้างคำถามที่เฉพาะเจาะจงสำหรับฉัน แต่ฉันรู้ว่ามันจะเป็นคำถามที่ถูกถามอย่างกว้างขวางเช่นกัน
Dan Atkinson

2
วิธีแก้ปัญหาที่แนะนำไม่สามารถใช้งานได้ใน MVC 3
Kasper Holdum

1
@Qua: ทางออกที่แนะนำมีอายุมากกว่าสองปี ฉันไม่คิดว่ามันจะทำงานกับ 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
ฉันคิดว่าคุณอาจต้องลบ "คงที่" ออกจากการประกาศวิธีการของรุ่นมีดโกนมิฉะนั้นจะไม่พบ ControllerContext และคณะ
Mike

3
คุณจะต้องใช้วิธีการกำจัดของคุณเองสำหรับช่องว่างที่ไม่จำเป็นเหล่านั้น วิธีที่ดีที่สุดที่ฉันสามารถคิดได้จากส่วนบนของหัวของฉันคือการโหลดสตริงลงใน XmlDocument แล้วเขียนกลับไปที่สตริงที่มี XmlWriter ตามลิงก์ที่ฉันทิ้งไว้ในความคิดเห็นล่าสุดของฉัน ฉันหวังว่าจะช่วยได้จริงๆ
เบ็นเลช

3
อืมฉันจะทำสิ่งนี้ได้อย่างไรโดยใช้ WebApi Controller คำแนะนำใด ๆ ที่จะได้รับการชื่นชม
Alexander

3
สวัสดีทุกคนที่จะใช้มันด้วยคำสำคัญ "คงที่" สำหรับตัวควบคุมทั้งหมดที่จะทำให้มันเป็นเรื่องธรรมดาที่คุณจะต้องทำให้ชั้นคงที่และภายในนั้นคุณจะต้องใส่วิธีนี้ด้วย "this" เป็นพารามิเตอร์เพื่อ "ControllerContext" คุณสามารถดูที่นี่stackoverflow.com/a/18978036/2318354มัน
Dilip0165

68

คำตอบนี้ไม่ได้อยู่ในทางของฉัน นี่คือต้นกำเนิดมาจาก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);

ฉันหวังว่านี่จะเป็นประโยชน์กับคุณในการทำโค้ดให้สะอาดและเรียบร้อย


1
ทางออกที่ดี! สิ่งหนึ่งที่ RenderRazorViewToString เป็นวิธีการขยายจริง ๆ (เพราะคุณผ่านพารามิเตอร์ควบคุมด้วยคำหลักนี้) ดังนั้นวิธีการขยายนี้สามารถเรียกวิธีนี้: this.RenderRazorViewToString ("ViewName" รุ่น);
Sergey

@ Sergey อืม ... ให้ฉันตรวจสอบด้วยวิธีนี้ถ้ามันดีกว่าฉันจะอัพเดตคำตอบของฉัน อย่างไรก็ตามขอขอบคุณสำหรับคำแนะนำของคุณ
Dilip0165

Dilip0165 ฉันได้รับข้อผิดพลาดในการอ้างอิง null บน var viewResult = ViewEngines.Engines.FindPartialView (controller.ControllerContext, viewName); คุณมีความคิดใด ๆ
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

ขออภัยยังคงคิดเกี่ยวกับ Silverlight เมื่อปีที่แล้วซึ่งมี rc แรกคือ 0. :) ฉันให้ภาพนี้ในวันนี้ (เร็วที่สุดเท่าที่ผมทำงานออกมาในรูปแบบที่ถูกต้องของเส้นทางมุมมอง)
NikolaiDante

สิ่งนี้ยังคงหยุดพักการเปลี่ยนเส้นทางใน RC1
พ่ายแพ้

พ่ายแพ้: ไม่มันไม่ได้ ถ้าเป็นเช่นนั้นคุณกำลังทำอะไรผิด
Dan Atkinson

ผสานสิ่งนี้เข้ากับstackoverflow.com/questions/520863/ …เพิ่มการรับรู้ของ ViewEnginesCollection พยายามที่จะแสดงบางส่วนและรับstackoverflow.com/questions/520863/นี้ : E
Arnis Lapsa

31

ฉันพบโซลูชันใหม่ที่ทำให้มุมมองเป็นสตริงโดยไม่ต้องยุ่งกับกระแสการตอบสนองของ 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 ")

Update:ใน 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);

//...

ไม่มีวิธี RenderPartial บนวัตถุ HtmlHelper สิ่งนี้เป็นไปไม่ได้ - html.RenderPartial (viewName, viewData);
MartinF

1
ใน ASP.NET MVC รีลีส 1.0 มีเมธอดส่วนขยาย RenderPartial สองสามรายการ สิ่งที่ฉันใช้เป็นพิเศษคือ System.Web.Mvc.Html.RenderPartialExtensions.RenderPartial (HtmlHelper นี้สตริงวัตถุ) ฉันไม่ทราบว่ามีการเพิ่มวิธีนี้ในการแก้ไขล่าสุดของ MVC หรือไม่
LorenzCK

ขอบคุณ เพียงแค่ต้องการที่จะเพิ่ม namespace System.Web.Mvc.Html ไปใช้ประกาศ (อื่น html.RenderPartial ( .. ) แน่นอนเคยชินจะสามารถเข้าถึงได้ :))
MartinF

ทุกคนมีสิ่งนี้ทำงานร่วมกับ RC ของ MVC2 หรือไม่? พวกเขาเพิ่มพารามิเตอร์ Textwriter เพิ่มเติมลงใน ViewContext ฉันพยายามเพิ่ม StringWriter ใหม่ () แต่มันไม่ทำงาน
beckelmw

1
@beckelmw: ฉันปรับปรุงการตอบสนอง คุณต้องส่งต้นฉบับที่StringWriterคุณใช้เพื่อเขียนไปยังStringBuilderอินสแตนซ์ใหม่มิเช่นนั้นผลลัพธ์ของมุมมองจะหายไป
LorenzCK

11

บทความนี้อธิบายวิธีการแสดงเป็นสตริงในสถานการณ์ต่าง ๆ :

  1. MVC Controller เรียกใช้ ActionMethods ของตนเอง
  2. ตัวควบคุม MVC เรียกใช้ ActionMethod ของตัวควบคุม MVC อื่น
  3. WebAPI Controller เรียกใช้ ActionMethod ของ MVC Controller

วิธีการแก้ปัญหา / รหัสให้เป็นระดับที่เรียกว่าViewRenderer มันเป็นส่วนหนึ่งของริก Stahl ของWestwindToolkit ที่ GitHub

การใช้งาน (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);

สิ่งนี้ใช้เครื่องยนต์มีดโกนโอเพนซอร์สที่ยอดเยี่ยมที่นี่: https://github.com/Antaris/RazorEngine


ดี! คุณรู้หรือไม่ว่ามีเอ็นจิ้นการวิเคราะห์คำที่คล้ายกันสำหรับไวยากรณ์ WebForms ฉันยังคงมีมุมมอง WebForms เก่าที่ยังไม่สามารถย้ายไปยังมีดโกนได้เลย
Dan Atkinson

สวัสดีฉันมีปัญหามากมายกับ razorengine และการรายงานข้อผิดพลาดไม่ค่อยดีนัก ฉันไม่คิดว่าผู้ช่วยของ 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 และมีดโกนจากเว็บไซต์อื่นมันใช้งานได้สำหรับฉัน:

    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 renderer - MVC3 View Render to String


ใช่นี่เป็นคำตอบที่ได้รับการยอมรับมากขึ้นหรือน้อยลง :)
แดนแอตกินสัน

2

ในการแสดงมุมมองสตริงใน Service Layer โดยไม่ต้องผ่าน 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

เคล็ดลับด่วน

สำหรับรุ่นที่พิมพ์อย่างยิ่งเพียงเพิ่มลงในคุณสมบัติ 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);

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 ฉันใช้มันเพื่อให้ฉันสามารถสร้างอีเมล 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 ไม่จำเป็นต้องมีคอนโทรลเลอร์

นี่คือตัวอย่างของรหัสในตัวอย่างนี้ฉันจำลอง mvc action ด้วย async http handler:

    /// <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.