【问题标题】:Rendering a view to a string in MVC, then redirecting -- workarounds?将视图呈现到 MVC 中的字符串,然后重定向 - 解决方法?
【发布时间】:2010-11-19 20:48:21
【问题描述】:

尽管this answer from Feb(我认为是 1.0 版之后)声称这是可能的,但我无法将视图呈现到字符串然后重定向。我以为我做错了什么,然后我读了这个 answer from Haack in July 声称这是不可能的。

如果有人让它工作并且可以帮助我让它工作,那就太好了(我会发布代码和错误)。但是,我现在需要解决方法。有一些,但没有什么理想的。有人解决了这个问题,或者对我的想法有任何意见吗?

  1. 这是为了呈现电子邮件。虽然我肯定可以在 Web 请求之外发送电子邮件(将信息存储在数据库中并稍后获取),但电子邮件的类型很多,我不想存储模板数据(用户对象、其他一些 LINQ 对象) ) 在数据库中让它稍后呈现。我可以创建一个更简单、可序列化的 POCO 并将其保存在数据库中,但为什么呢? ...我只想渲染文本!
  2. 我可以创建一个新的 RedirectToAction 对象来检查标头是否已发送(不知道如何执行此操作 - try/catch?),如果是,则构建一个带有元重定向的简单页面,一个 javascript 重定向,还有一个“点击这里”链接。
  3. 在我的控制器中,我可以记住我是否呈现了一封电子邮件,如果是,则通过显示视图手动执行 #2。
  4. 我可以在任何潜在的电子邮件呈现之前手动发送重定向标头。然后,我不使用 MVC 基础结构来重定向操作,而是调用 result.end。这似乎最简单,但确实很混乱。
  5. 还有别的吗?

编辑:我尝试了 Dan 的代码(与我已经尝试过的 1 月/2 月的代码非常相似),但我仍然遇到同样的错误。我能看到的唯一实质性区别是他的示例使用了视图,而我使用了局部视图。稍后我将尝试使用视图对此进行测试。

这是我得到的:

控制器

public ActionResult Certifications(string email_intro)
        {
            //a lot of stuff

            ViewData["users"] = users;

            if (isPost())
            {
                //create the viewmodel
                var view_model = new ViewModels.Emails.Certifications.Open(userContext)
                {
                    emailIntro = email_intro
                };

                //i've tried stopping this after just one iteration, in case the problem is due to calling it multiple times
                foreach (var user in users)
                {
                    if (user.Email_Address.IsValidEmailAddress())
                    {
                        //add more stuff to the view model specific to this user
                        view_model.user = user;
                        view_model.certification302Summary.subProcessesOwner = new SubProcess_Certifications(RecordUpdating.Role.Owner, null, null, user.User_ID, repository);
                        //more here....

                        //if i comment out the next line, everything works ok
                        SendEmail(view_model, this.ControllerContext);
                    }
                }

                return RedirectToAction("Certifications");
            }

            return View();
        }

SendEmail()

   public static void SendEmail(ViewModels.Emails.Certifications.Open model, ControllerContext context)
        {
            var vd = context.Controller.ViewData;
            vd["model"] = model;
            var renderer = new CustomRenderers();
            //i fixed an error in your code here
            var text = renderer.RenderViewToString3(context, "~/Views/Emails/Certifications/Open.ascx", "", vd, null);
            var a = text;
        }

自定义渲染器

public class CustomRenderers
    {
        public virtual string RenderViewToString3(ControllerContext controllerContext, string viewPath, string masterPath, ViewDataDictionary viewData, TempDataDictionary tempData)
        {
            //copy/paste of dan's code
        }
    }

错误

[HttpException (0x80004005): Cannot redirect after HTTP headers have been sent.]
   System.Web.HttpResponse.Redirect(String url, Boolean endResponse) +8707691

谢谢, 詹姆斯

【问题讨论】:

  • 你找到解决办法了吗?

标签: asp.net-mvc rendering redirecttoaction httpexception


【解决方案1】:
public Action SendEmail(int id)
{
  //Let's say that id is the db id of an order that a customer has just placed.

  //Go get that model from the db.
  MyModel model = new Model(id);

  //Now send that email. Don't forget the model and controller context.
  SendEmail(model, this.ControllerContext);

  //Render (or redirect!)
  return RedirectToAction("Wherever");
}

private static void SendEmail(MyModel model, ControllerContext controllerContext)
{
  //Recreate the viewdata
  ViewDataDictionary viewData = controllerContext.Controller.ViewData;
  viewData["Order"] = model;
  string renderedView = "";
  CustomRenderers customRenderers = new CustomRenderers();

  //Now render the view to string
  //ControllerContext, ViewPath, MasterPath, ViewDataDictionary, TempDataDictionary
  //As you can see, we're not passing a master page, and the tempdata is in this instance.
  renderedView = RenderViewToString(controllerContext, "~/Views/Orders/Email.aspx", "", viewData, null);

  //Now send your email with the string as the body.
  //Not writing that, as the purpose is just to show the rendering. :)
}


//Elsewhere...
public class CustomRenderers
{
  public virtual string RenderViewToString(ControllerContext controllerContext, string viewPath, string masterPath, ViewDataDictionary viewData, TempDataDictionary tempData)
  {
    if (tempData == null)
    {
    tempData = new TempDataDictionary();
    }

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

在您的 Orders/Email.aspx 视图中,确保您引用 ViewData 中的所有内容,而不是模型。你可以这样做:

<% MyModel model = (MyModel)ViewData["Order"] %>

【讨论】:

  • Dan -- 感谢您的尝试,但在使用您 Jan 发布的代码后,我遇到了相同的“无法重定向错误”。我已经在我的问题中粘贴了所有内容。我究竟做错了什么?谢谢。
【解决方案2】:

这是一种将视图呈现为字符串的替代方法,该字符串永远不会导致数据输出到响应中(因此它应该避免您的问题):http://craftycodeblog.com/2010/05/15/asp-net-mvc-render-partial-view-to-string/

要呈现常规视图而不是局部视图,您需要将“ViewEngines.Engines.FindPartialView”更改为“ViewEngines.Engines.FindView”。

【讨论】:

    【解决方案3】:

    已更新。

    现在我了解到您想使用视图引擎以 html 格式生成实际的电子邮件,我提出以下建议:

    在控制器中将动作呈现为文本的代码: http://mikehadlow.blogspot.com/2008/06/mvc-framework-capturing-output-of-view_05.html

    对您的代码进行少量修改:

    public ActionResult Certifications(string email_intro)
    {
         //a lot of stuff              
         ViewData["users"] = users;              
         if (isPost())             
         {                 
             //create the viewmodel                 
             var view_model = new ViewModels.Emails.Certifications.Open(userContext) { emailIntro = email_intro };                  
    
             foreach (var user in users)                 
             {                     
                 if (user.Email_Address.IsValidEmailAddress())                     
                 {                         
                     view_model.user = user;                         
                     view_model.certification302Summary.subProcessesOwner = new SubProcess_Certifications(RecordUpdating.Role.Owner, null, null, user.User_ID, repository);                         
    
                     SendEmail(view_model);                     
                 }                 
             }                  
             return RedirectToAction("Certifications");             
         }              
         return View();         
     } 
    
     public void SendEmail(ViewModels.Emails.Certifications.Open model)         
     {             
        var vd = context.Controller.ViewData;             
        vd["model"] = model;             
        var renderer = new CustomRenderers();             
    
        // Implement the actual email rendering as a regular action method on this controller
        var text = this.CaptureActionHtml(c => (ViewResult)c.RenderEmail(model));
    
        var a = text;         
    } 
    

    【讨论】:

    • 你会建议我如何实现我在示例中尝试做的事情?即,我想使用模板引擎来创建电子邮件,而不是将它们和一堆 HTML 连接在一起。
    • 谢谢...我会在一两个月后回到那个项目时看看这个。
    猜你喜欢
    • 1970-01-01
    • 2010-11-12
    • 1970-01-01
    • 2011-01-28
    • 2016-10-10
    • 2013-09-28
    相关资源
    最近更新 更多