Bagaimana cara membuat tampilan ASP.NET MVC sebagai string?

485

Saya ingin menampilkan dua tampilan berbeda (satu sebagai string yang akan dikirim sebagai email), dan yang lainnya halaman ditampilkan kepada pengguna.

Apakah ini mungkin dalam ASP.NET MVC beta?

Saya sudah mencoba beberapa contoh:

1. RenderPartial to String di ASP.NET MVC Beta

Jika saya menggunakan contoh ini, saya menerima "Tidak dapat mengarahkan ulang setelah tajuk HTTP dikirim."

2. Kerangka MVC: Menangkap output dari tampilan

Jika saya menggunakan ini, saya tampaknya tidak dapat melakukan redirectToAction, karena mencoba membuat tampilan yang mungkin tidak ada. Jika saya mengembalikan tampilan, itu benar-benar kacau dan tidak terlihat benar sama sekali.

Adakah yang punya ide / solusi untuk masalah ini yang saya miliki, atau punya saran untuk yang lebih baik?

Terimakasih banyak!

Di bawah ini adalah contohnya. Apa yang saya coba lakukan adalah membuat metode 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);
}

Jawaban yang diterima dari Tim Scott (diubah dan diformat sedikit oleh saya):

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

Contoh penggunaan

Dengan asumsi panggilan dari pengontrol untuk mendapatkan email konfirmasi pesanan, melewati lokasi Site.Master.

string myString = RenderViewToString(this.ControllerContext, "~/Views/Order/OrderResultEmail.aspx", "~/Views/Shared/Site.Master", this.ViewData, this.TempData);
Dan Atkinson
sumber
2
Bagaimana Anda bisa menggunakan ini dengan tampilan, yang sangat diketik? Yaitu. bagaimana saya bisa memberi makan model ke halaman?
Kjensen
Tidak dapat menggunakan ini dan membuat JsonResult sesudahnya, karena tipe konten tidak dapat diatur setelah header dikirim (karena Flush mengirimkannya).
Arnis Lapsa
Karena tidak ada jawaban yang benar, saya kira. :) Saya membuat pertanyaan yang khusus untuk saya, tetapi saya tahu bahwa itu akan menjadi pertanyaan yang banyak ditanyakan juga.
Dan Atkinson
2
Solusi yang disarankan tidak bekerja di MVC 3.
Kasper Holdum
1
@ Qua: Solusi yang disarankan sudah lebih dari dua tahun. Saya juga tidak berharap itu bekerja untuk MVC 3! Selain itu, ada cara yang lebih baik untuk melakukan ini sekarang.
Dan Atkinson

Jawaban:

572

Inilah yang saya buat, dan itu berhasil untuk saya. Saya menambahkan metode berikut ke kelas basis pengontrol saya. (Anda selalu dapat membuat metode statis ini di tempat lain yang menerima controller sebagai parameter saya kira)

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

Pisau cukur .cshtml style

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

Edit: menambahkan kode Razor.

Ben Lesh
sumber
31
Merender tampilan ke string selalu "tidak konsisten dengan konsep seluruh rute", karena tidak ada hubungannya dengan routing. Saya tidak yakin mengapa jawaban yang berhasil mendapat suara turun.
Ben Lesh
4
Saya pikir Anda mungkin perlu menghapus "statis" dari deklarasi metode versi Razor, jika tidak ia tidak dapat menemukan ControllerContext et al.
Mike
3
Anda harus menerapkan metode penghapusan sendiri untuk ruang putih berlebihan itu. Cara terbaik yang dapat saya pikirkan dari atas kepala saya adalah dengan memuat string ke dalam XmlDocument, kemudian menulisnya kembali ke string dengan XmlWriter, sesuai tautan yang saya tinggalkan di komentar terakhir saya. Saya sangat berharap itu membantu.
Ben Lesh
3
Hmm bagaimana saya melakukan ini menggunakan WebApi Controller, saran apa pun akan dihargai
Alexander
3
Hai semua orang untuk menggunakannya dengan kata kunci "Statis" untuk semua pengendali agar menjadi umum, Anda harus membuat kelas statis dan di dalamnya Anda harus meletakkan metode ini dengan "ini" sebagai parameter ke "ControllerContext". Anda bisa lihat di sini stackoverflow.com/a/18978036/2318354 itu.
Dilip0165
68

Jawaban ini tidak dalam perjalanan saya. Ini awalnya dari https://stackoverflow.com/a/2759898/2318354 tetapi di sini saya telah menunjukkan cara untuk menggunakannya dengan Kata Kunci "Statis" untuk menjadikannya umum bagi semua Pengontrol.

Untuk itu Anda harus membuat staticfile kelas di kelas. (Misalkan Nama File Kelas Anda adalah Utils.cs)

Contoh ini adalah Untuk Pisau Cukur.

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

Sekarang Anda dapat memanggil kelas ini dari controller Anda dengan menambahkan NameSpace di File Controller Anda sebagai cara berikut dengan meneruskan "this" sebagai parameter ke Controller.

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

Seperti saran yang diberikan oleh @Sergey, metode ekstensi ini juga dapat memanggil dari cotroller seperti yang diberikan di bawah ini

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

Saya harap ini akan berguna untuk Anda membuat kode bersih dan rapi.

Dilip0165
sumber
1
Solusi bagus! Satu hal, RenderRazorViewToString sebenarnya adalah metode ekstensi (karena Anda melewatkan parameter pengontrol dengan kata kunci ini), sehingga metode ekstensi ini dapat disebut sebagai ini: this.RenderRazorViewToString ("ViewName", model);
Sergey
@Sergey Hmmm ... Biarkan saya memeriksa dengan cara itu, jika tidak masalah saya akan memperbarui jawaban saya. Bagaimanapun terima kasih atas saran Anda.
Dilip0165
Dilip0165, saya mendapat kesalahan referensi nol pada var viewResult = ViewEngines.Engines.FindPartialView (controller.ControllerContext, viewName) ;. Apakah kamu punya ide?
CB4
@ CB4 Saya pikir ini bisa menjadi masalah "viewName" yang Anda operasikan. Anda harus melewati "viewName" dengan path lengkap sesuai struktur folder Anda. Jadi, periksa hal ini.
Dilip0165
1
@Sergey Terima kasih atas saran Anda, saya telah memperbarui jawaban saya sesuai saran Anda yang benar-benar benar
Dilip0165
32

Ini bekerja untuk saya:

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;
    }
}
Tim Scott
sumber
Terima kasih atas komentar Anda, tetapi bukankah itu digunakan untuk merender di dalam tampilan? Bagaimana saya bisa menggunakannya dalam konteks yang saya perbarui pertanyaannya?
Dan Atkinson
Maaf, masih memikirkan Silverlight tahun lalu yang rc pertamanya 0. :) Saya mencoba ini hari ini. (Segera setelah saya menemukan format jalur pandangan yang benar)
NikolaiDante
Ini masih memecah pengalihan di RC1
dikalahkan
dikalahkan: Tidak, tidak. Jika ya, berarti Anda melakukan kesalahan.
Dan Atkinson
Menggabungkan ini dengan stackoverflow.com/questions/520863/… , menambah kesadaran akan ViewEnginesCollection, mencoba memunculkan partialview dan mendapatkan stackoverflow.com/questions/520863/… ini . : E
Arnis Lapsa
31

Saya menemukan solusi baru yang membuat tampilan menjadi string tanpa harus mengacaukan aliran Respon dari HttpContext saat ini (yang tidak memungkinkan Anda untuk mengubah ContentType atau header lainnya dari respons).

Pada dasarnya, semua yang Anda lakukan adalah membuat HttpContext palsu agar tampilan dapat dirender sendiri:

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

Ini berfungsi pada ASP.NET MVC 1.0, bersama dengan ContentResult, JsonResult, dll. (Mengubah Header pada HttpResponse asli tidak membuang " Server tidak dapat mengatur tipe konten setelah header HTTP telah dikirim " pengecualian).

Pembaruan: di ASP.NET MVC 2.0 RC, kode sedikit berubah karena kita harus meneruskan yang StringWriterdigunakan untuk menulis tampilan ke 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);

//...
LorenzCK
sumber
Tidak ada metode RenderPartial pada objek HtmlHelper. Ini tidak mungkin - html.RenderPartial (viewName, viewData);
MartinF
1
Dalam rilis ASP.NET MVC 1.0 ada beberapa metode ekstensi RenderPartial. Yang saya gunakan secara khusus adalah System.Web.Mvc.Html.RenderPartialExtensions.RenderPartial (ini HtmlHelper, string, objek). Saya tidak tahu apakah metode ini telah ditambahkan dalam revisi terbaru MVC dan tidak ada di yang sebelumnya.
LorenzCK
Terima kasih. Hanya perlu menambahkan namespace System.Web.Mvc.Html untuk menggunakan deklarasi (yang lain Html.RenderPartial (..) tentu wont dapat diakses :))
MartinF
Apakah ada yang punya ini bekerja dengan RC MVC2? Mereka menambahkan parameter Textwriter tambahan ke ViewContext. Saya mencoba menambahkan StringWriter baru (), tetapi tidak berhasil.
beckelmw
1
@beckelmw: Saya memperbarui jawabannya. Anda harus memasukkan yang asli yang StringWriterAnda gunakan untuk menulis StringBuilder, bukan contoh baru atau output tampilan akan hilang.
LorenzCK
11

Artikel ini menjelaskan cara merender tampilan ke string dalam skenario yang berbeda:

  1. MVC Controller memanggil ActionMethods lainnya
  2. MVC Controller memanggil ActionMethod dari MVC Controller lainnya
  3. Kontroler WebAPI memanggil Metode Aksi dari Kontroler MVC

Solusi / kode disediakan sebagai kelas yang disebut ViewRenderer . Ini adalah bagian dari WestwindToolkit milik Rick Stahl di GitHub .

Penggunaan (3. - Contoh WebAPI):

string html = ViewRenderer.RenderView("~/Areas/ReportDetail/Views/ReportDetail/Index.cshtml", ReportVM.Create(id));
Jenny O'Reilly
sumber
3
Juga sebagai paket NuGet West Wind Web MVC Utilities ( nuget.org/packages/Westwind.Web.Mvc ). Sebagai bonus, penyaji tampilan tidak hanya dapat membuat tampilan parsial, tetapi juga seluruh tampilan termasuk Tata Letak. Artikel blog dengan kode: weblog.west-wind.com/posts/2012/May/30/…
Jeroen K
Akan lebih bagus jika ini dipecah menjadi paket yang lebih kecil. Paket Nuget membuat banyak perubahan pada web.config Anda dan menambahkan file js ke proyek Anda, yang kemudian tidak dibersihkan ketika Anda mencopotnya: /
Josh Noe
8

Jika Anda ingin melupakan MVC sepenuhnya, maka hindari semua kekacauan 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);

Ini menggunakan Mesin Silet open source yang mengagumkan di sini: https://github.com/Antaris/RazorEngine

Josh Noe
sumber
Bagus! Apakah Anda tahu jika ada mesin parsing serupa untuk sintaks WebForms? Saya masih memiliki beberapa tampilan WebForms lama yang belum dapat dipindahkan ke Razor.
Dan Atkinson
Hai, saya punya banyak masalah dengan razorengine, dan pelaporan kesalahannya tidak terlalu bagus. Saya tidak berpikir penolong Url didukung
Layinka
@Layinka Tidak yakin apakah ini membantu, tetapi sebagian besar info kesalahan ada di CompilerErrorsproperti pengecualian.
Josh Noe
5

Anda mendapatkan tampilan dalam string menggunakan cara ini

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

Kami memanggil metode ini dalam dua cara

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

ATAU

var model = new Person()
string strView = RenderPartialViewToString("~/Views/Shared/_Header.cshtml", model)
Jayesh Patel
sumber
4

Kiat tambahan untuk ASP NET CORE:

Antarmuka:

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

Penerapan:

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

Pendaftaran di Startup.cs

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

Dan penggunaan dalam controller:

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);
  }
}
Marcin
sumber
3

Saya menggunakan MVC 1.0 RTM dan tidak ada solusi di atas yang berfungsi untuk saya. Tapi yang ini:

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
Jeremy Bell
sumber
2

Saya melihat implementasi untuk MVC 3 dan Razor dari situs web lain, itu berhasil untuk saya:

    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;

        }

    }

Lebih lanjut tentang Razor render- MVC3 View Render to String

Adamy
sumber
Ya, ini sebenarnya adalah salinan dari jawaban yang diterima. :)
Dan Atkinson
2

Untuk membuat tampilan ke string di Lapisan Layanan tanpa harus melewati ControllerContext sekitar, ada artikel Rick Strahl yang baik di sini http://www.codemag.com/Article/1312081 yang membuat pengendali generik. Ringkasan kode di bawah ini:

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

Lalu untuk menyajikan Tampilan di kelas Layanan:

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

Kiat cepat

Untuk Model yang diketik dengan kuat, tambahkan saja ke properti ViewData.Model sebelum meneruskan ke RenderViewToString. misalnya

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

Untuk mengulang dari pertanyaan yang lebih tidak dikenal, lihat MvcIntegrationTestFramework .

Itu membuat Anda tidak menulis pembantu sendiri untuk mengalirkan hasil dan terbukti bekerja cukup baik. Saya menganggap ini akan menjadi proyek uji dan sebagai bonus Anda akan memiliki kemampuan pengujian lainnya setelah Anda mendapatkan pengaturan ini. Gangguan utama mungkin akan memilah-milah rantai ketergantungan.

 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"));
        });
}
merpati
sumber
0

Berikut adalah kelas yang saya tulis untuk melakukan ini untuk ASP.NETCore RC2. Saya menggunakannya sehingga saya bisa menghasilkan email html menggunakan 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();
            }
        }
    }
}
Joe Audette
sumber
0

Saya menemukan cara yang lebih baik untuk membuat halaman tampilan pisau cukur ketika saya mendapatkan kesalahan dengan metode di atas, solusi ini untuk lingkungan bentuk web dan lingkungan mvc. Tidak diperlukan pengontrol.

Berikut adalah contoh kode, dalam contoh ini saya mensimulasikan tindakan mvc dengan 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());
        }
    }
dexiang
sumber