Warning: file_get_contents(/data/phpspider/zhask/data//catemap/7/kubernetes/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Asp.net mvc 如何将ASP.NET MVC视图呈现为字符串?_Asp.net Mvc_Rendering - Fatal编程技术网

Asp.net mvc 如何将ASP.NET MVC视图呈现为字符串?

Asp.net mvc 如何将ASP.NET MVC视图呈现为字符串?,asp.net-mvc,rendering,Asp.net Mvc,Rendering,我想输出两个不同的视图(一个作为字符串,将作为电子邮件发送),另一个是显示给用户的页面 这在ASP.NET MVC测试版中可能吗 我试过多个例子: 1. 如果我使用这个例子,我会收到“HTTP后无法重定向”的消息 标题已发送。“ 2. 如果我使用它,我似乎无法执行重定向操作,因为 尝试渲染可能不存在的视图。如果我返回视图,它将 完全搞砸了,看起来一点也不对劲 有没有人对我提出的这些问题有什么想法/解决方案,或者有什么更好的建议 非常感谢 下面是一个例子。我要做的是创建GetViewForEmai

我想输出两个不同的视图(一个作为字符串,将作为电子邮件发送),另一个是显示给用户的页面

这在ASP.NET MVC测试版中可能吗

我试过多个例子:

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);
}
接受了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;
    }
}
示例用法

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();
  }
}
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();
  }
}
假设控制器打电话获取订单确认电子邮件,传递Site.Master位置

string myString = RenderViewToString(this.ControllerContext, "~/Views/Order/OrderResultEmail.aspx", "~/Views/Shared/Site.Master", this.ViewData, this.TempData);
这对我很有用:

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;
    }
}
快速提示

对于强类型模型,只需将其添加到ViewData.Model属性,然后再传递到RenderViewToString。e、 g

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

我使用的是MVC1.0RTM,上面的解决方案都不适合我。但这一次:

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

我发现了一个新的解决方案,它将视图呈现为字符串,而不必弄乱当前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
}

要从一个更未知的问题重复,请查看

它可以节省您编写自己的助手来流式处理结果的时间,并被证明可以很好地工作。我假设这将是在一个测试项目中,并且作为奖励,一旦你有了这个设置,你将拥有其他的测试功能。主要的麻烦可能是整理依赖链

 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"));
        });
}
private静态只读字符串mvcAppPath=
Path.GetFullPath(AppDomain.CurrentDomain.BaseDirectory
+“\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\;
私有只读AppHost AppHost=新AppHost(mvcAppPath);
[测试]
public void Root\u Url\u呈现\u索引\u视图()
{
appHost.SimulateBrowsingSession(浏览会话=>{
RequestResult=browsingSession.ProcessRequest(“”);

Assert.IsTrue(result.ResponseText.Contains)(“这是我想到的,它对我有用。我在我的控制器基类中添加了以下方法。(我想你可以在其他地方创建这些静态方法,它们接受控制器作为参数)

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

编辑:添加了Razor代码。

我在另一个网站上看到了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;

        }

    }

如果您想完全放弃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引擎:

这个答案不在我的路上。这是我最初的答案,但我在这里展示了如何将它与“Static”关键字一起使用,以使它对所有控制器通用

为此,您必须在类文件中创建
静态
类。(假设您的类文件名为Utils.cs)

此示例适用于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”作为参数传递给控制器

string result = RazorViewToString.RenderRazorViewToString(this ,"ViewName", model);
根据@Sergey的建议,此扩展方法也可以从cotroller调用,如下所示

string result = this.RenderRazorViewToString("ViewName", model);
我希望这将有助于您使代码干净整洁。

介绍了如何在不同场景中呈现字符串视图:

  • MVC控制器调用另一个自己的ActionMethods
  • MVC控制器调用另一个MVC控制器的ActionMethod
  • WebAPI控制器调用MVC控制器的ActionMethod
  • 该解决方案/代码作为名为ViewRenderer的类提供。它是Rick Stahl的一部分

    用法(3.-WebAPI示例):


    您可以使用这种方式获取字符串中的视图

    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)
    


    下面是我为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();
                }
            }
        }
    }
    
    使用Microsoft.AspNetCore.Http;
    使用Microsoft.AspNetCore.Mvc;
    使用Microsoft.AspNetCore.Mvc.Abstractions;
    使用Microsoft.AspNetCore.Mvc.ModelBinding;
    使用Microsoft.AspNetCore.Mvc.Rendering;
    使用Microsoft.AspNetCore.Mvc.ViewEngines;
    使用Microsoft.AspNetCore.Mvc.ViewFeatures;
    使用Microsoft.AspNetCore.Routing;
    使用System.IO;
    使用System.Threading.Tasks;
    命名空间cloudscribe.Web.Common.Razor
    {
    /// 
    ///这个类的目标是提供一种使用
    ///Razor模板和模型,用于生成html电子邮件。
    /// 
    公共类视图渲染器
    {
    公共视图渲染器(
    ICompositeViewEngine视图引擎,
    ITempDataProvider tempDataProvider,
    IHttpContextAccessor(上下文访问器)
    {
    this.viewEngine=viewEngine;
    this.tempDataProvider=tempDataProvider;
    this.contextAccesor=contextAccesor;
    }
    私有ICompositeViewEngine视图引擎;
    私有ITempDataProvider tempDataProvider;
    私有IHttpContextAccessor contextAccesor;
    公共异步任务RenderViewAsString(字符串视图名,TModel模型)
    {
    var viewData=新的ViewDataDictionary(
    metadataProvider:new EmptyModelMetadataProvider(),
    modelState:新的ModelStateDictionary())
    {
    模型
    };
    var actionContext=new actionContext(contextAccesor.HttpContext,new RouteData(),new ActionDescriptor(
    
    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();
                }
            }
        }
    }
    
        /// <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());
            }
        }
    
    // 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);