asp.net mvc - एक स्ट्रिंग के रूप में एक दृश्य प्रस्तुत करें




asp.net-mvc rendering (10)

मैं दो अलग-अलग विचारों को आउटपुट करना चाहता हूं (एक स्ट्रिंग के रूप में जो एक ईमेल के रूप में भेजा जाएगा), और दूसरा पृष्ठ किसी उपयोगकर्ता को प्रदर्शित किया गया है।

क्या यह एएसपी.नेट एमवीसी बीटा में संभव है?

मैंने कई उदाहरणों की कोशिश की है:

1. एएसपी.नेट एमवीसी बीटा में स्ट्रिंगपार्टियल टू स्ट्रिंग

अगर मैं इस उदाहरण का उपयोग करता हूं, तो मुझे "HTTP शीर्षलेख भेजे जाने के बाद पुनर्निर्देशित नहीं किया जा सकता है"।

2. एमवीसी फ्रेमवर्क: दृश्य के आउटपुट को कैप्चर करना

अगर मैं इसका उपयोग करता हूं, तो मुझे रीडायरेक्ट टॉक्शन करने में असमर्थ लगता है, क्योंकि यह उस दृश्य को प्रस्तुत करने का प्रयास करता है जो मौजूद नहीं हो सकता है। अगर मैं दृश्य वापस कर देता हूं, तो यह पूरी तरह से गड़बड़ हो गया है और बिल्कुल सही नहीं दिखता है।

क्या मेरे पास इन मुद्दों के लिए कोई विचार / समाधान है, या बेहतर लोगों के लिए कोई सुझाव है?

बहुत धन्यवाद!

नीचे एक उदाहरण है। मैं जो करने की कोशिश कर रहा हूं वह 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;
    }
}

उदाहरण उपयोग

साइट को पास करने, ऑर्डर पुष्टिकरण ईमेल प्राप्त करने के लिए नियंत्रक से एक कॉल मानना। मस्टर स्थान।

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

आपको इस तरह से स्ट्रिंग में दृश्य मिल रहा है

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)

एक और अज्ञात प्रश्न से दोहराने के लिए, MvcIntegrationTestFramework पर एक नज़र 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"));
        });
}

जल्द सलाह

दृढ़ता से टाइप किए गए मॉडल के लिए इसे केवल RendViewToString में जाने से पहले 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);

मुझे एक नया समाधान मिला जो वर्तमान HttpContext की प्रतिक्रिया धारा के साथ गड़बड़ किए बिना स्ट्रिंग को देखने के लिए प्रस्तुत करता है (जो आपको प्रतिक्रिया की सामग्री प्रकार या अन्य शीर्षकों को बदलने की अनुमति नहीं देता है)।

असल में, आप जो भी करते हैं, वह खुद को प्रस्तुत करने के लिए एक नकली 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
}

यह एएसपी.नेट एमवीसी 1.0 पर काम करता है, साथ ही ContentResult, JsonResult, आदि के साथ (मूल HttpResponse पर हेडर बदलना " सर्वर हेडर भेजे जाने के बाद सर्वर प्रकार सेट नहीं कर सकता " अपवाद नहीं फेंकता है)।

अद्यतन: एएसपी.नेट एमवीसी 2.0 आरसी में, कोड थोड़ा बदलता है क्योंकि हमें StringWriter में दृश्य लिखने के लिए प्रयुक्त StringWriter में पास करना होगा:

//...

//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.0 आरटीएम का उपयोग कर रहा हूं और उपर्युक्त समाधानों में से कोई भी मेरे लिए काम नहीं करता है। लेकिन यह एक किया था:

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 और रेजर के लिए एक कार्यान्वयन देखा, यह मेरे लिए काम किया:

    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;

        }

    }

रेजर रेंडर पर अधिक - एमवीसी 3 देखें स्ट्रिंग को रेंडर करें


यह जवाब मेरे रास्ते पर नहीं है। यह मूल रूप से https://.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();
        }
    }
}

अब आप इस नियंत्रक को पैरामीटर के रूप में "इस" को पास करके अपने कंट्रोलर फ़ाइल में नेमस्पेस जोड़कर अपने नियंत्रक से कॉल कर सकते हैं।

string result = RazorViewToString.RenderRazorViewToString(this ,"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;
    }
}

यहां मैं क्या आया, और यह मेरे लिए काम कर रहा है। मैंने अपने नियंत्रक बेस क्लास में निम्न विधि (ओं) को जोड़ा। (आप हमेशा इन स्थिर तरीकों को कहीं और बना सकते हैं जो नियंत्रक को पैरामीटर के रूप में स्वीकार करते हैं)

एमवीसी 2 .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();
  }
}

संपादित करें: रेजर कोड जोड़ा गया।


यह आलेख बताता है कि विभिन्न परिदृश्यों में एक स्ट्रिंग को दृश्य कैसे प्रस्तुत करना है:

  1. एमवीसी नियंत्रक अपने स्वयं के एक्शन मैथ्यूज को बुला रहा है
  2. एमवीसी नियंत्रक एक और एमवीसी नियंत्रक के एक एक्शन मोड को बुला रहा है
  3. वेबएपीआई नियंत्रक एक एमवीसी नियंत्रक के एक एक्शन मोड को बुला रहा है

समाधान / कोड को ViewRenderer नामक कक्षा के रूप में प्रदान किया जाता है। यह गिटहब में रिक स्टाल के वेस्टविंडटूलकिट का हिस्सा है

उपयोग (3. - वेबएपीआई उदाहरण):

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






rendering