asp.net-mvc - 将 View 呈现为 MVC 中的字符串,然后重定向——解决方法?

标签 asp.net-mvc rendering redirecttoaction httpexception

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

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

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

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

    这是我所拥有的:

    Controller
    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
    

    谢谢,
    詹姆士

    最佳答案

    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 View 中,确保您引用 ViewData 中的所有内容,而不是模型。你可以这样做:
    <% MyModel model = (MyModel)ViewData["Order"] %>
    

    关于asp.net-mvc - 将 View 呈现为 MVC 中的字符串,然后重定向——解决方法?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1281430/

    相关文章:

    asp.net - ModelClientValidationRule 冲突

    c# - 在一个解决方案中的两个项目中使用Ninject,出现循环依赖异常

    html - 在 MVC 和 jQuery Mobile 中基于设备宽度和高度重定向

    rendering - Three.js - 使用延迟渲染向区域光添加阴影

    c# - 在 MVC 中的实体中创建新对象的操作方法中的 RedirectToAction

    c# - 处理 session 以实现可伸缩性

    JavaFX 8 : rendering messed up on Linux

    javascript - Tracker afterFlush 函数出现异常 : undefined using Materialize modal

    c# - RedirectToAction 生成错误的 url(查询字符串参数而不是路由数据参数)

    .net - 在使用 ASP.NET MVC 的操作方法中使用哪种重定向方法进行 SEO? RedirectToAction 还是 RedirectToActionPermanent?