Отображение представления в строке в MVC, а затем обходные пути перенаправления?

Я не могу отобразить представление в строке, а затем перенаправить, несмотря на этот ответ от февраля (я думаю, после версии 1.0), которая утверждает, что это возможно. Я подумал, что делаю что-то не так, а потом прочитал ответ Хаака в июле, утверждающий, что это невозможно.

Если у кого-то это работает и может помочь мне заставить его работать, это здорово (и я опубликую код, ошибки). Однако сейчас мне нужны обходные пути. Есть несколько, но ничего идеального. Кто-нибудь решил это или есть комментарии по поводу моих идей?

  1. Это для отображения электронной почты. Хотя я могу точно отправить электронное письмо за пределами веб-запроса (сохранить информацию в базе данных и получить ее позже), существует много типов электронных писем, и я не хочу хранить данные шаблона (объект пользователя, несколько других объектов LINQ ) в БД, чтобы он был отрисован позже. Я мог бы создать более простой сериализуемый POCO и сохранить его в базе данных, но почему? ... Мне просто нужен визуализированный текст!
  2. Я могу создать новый объект RedirectToAction, который проверяет, были ли отправлены заголовки (не могу понять, как это сделать - попробуйте / поймать?), И, если да, строит простую страницу с мета-перенаправлением, перенаправлением javascript , а также ссылку «щелкните здесь».
  3. В моем контроллере я могу вспомнить, отображал ли я электронное письмо, и если да, то вручную выполняю №2, отображая представление.
  4. Я могу вручную отправить заголовки перенаправления перед любой потенциальной отправкой электронной почты. Затем, вместо того, чтобы использовать инфраструктуру MVC для перенаправления действия, я просто вызываю result.end. Это кажется самым простым, но действительно беспорядочным.
  5. Что-нибудь еще?

ИЗМЕНИТЬ: я пробовал код Дэна (очень похожий на код от января / февраля, который я уже пробовал), но все еще получаю ту же ошибку. Единственное существенное различие, которое я вижу, заключается в том, что в его примере используется представление, а я - частичное представление. Я попробую проверить это позже с целью.

Вот что у меня есть:

Контроллер

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

CustomRenderers

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

Спасибо, Джеймс


person James S    schedule 15.08.2009    source источник
comment
Вы когда-нибудь находили обходной путь для этого?   -  person Kirschstein    schedule 25.11.2009


Ответы (3)


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"] %>
person Dan Atkinson    schedule 15.08.2009
comment
Дэн - Спасибо за попытку, но я получаю ту же ошибку «Невозможно перенаправить», которую я получал после использования кода из вашей публикации Яна. Я вставил все в свой вопрос. Что я делаю неправильно? Спасибо. - person James S; 16.08.2009

Вот альтернативный метод рендеринга представления в строку, который никогда не приводит к выводу данных в ответ (поэтому он должен избегать вашей проблемы): http://craftycodeblog.com/2010/05/15/asp-net-mvc-render-partial-view-to-string/

Чтобы отобразить обычное представление вместо частичного, вам необходимо изменить ViewEngines.Engines.FindPartialView на ViewEngines.Engines.FindView.

person Kevin Craft    schedule 15.05.2010

Обновлено.

Теперь, когда я понимаю, что вы хотите использовать механизм просмотра для создания фактического электронного письма в 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;         
} 
person Thomas Eyde    schedule 30.12.2010
comment
Как вы посоветуете мне добиться того, что я пытался сделать в примере? то есть, я хочу использовать механизм шаблонов для создания электронных писем, а не объединять их и кучу HTML вместе. - person James S; 08.01.2011
comment
Спасибо ... Я посмотрю на это, когда вернусь к этому проекту через месяц или два. - person James S; 24.01.2011