Rendering a view to a string in MVC, then redirecting -- workarounds?
- by James S
Hi --
I can't render a view to a string and then redirect, despite this answer from Feb (after version 1.0, I think) that claims it's possible. I thought I was doing something wrong, and then I read this answer from Haack in July that claims it's not possible.
If somebody has it working and can help me get it working, that's great (and I'll post code, errors). However, I'm now at the point of needing workarounds. There are a few, but nothing ideal. Has anybody solved this, or have any comments on my ideas?
This is to render email. While I can surely send the email outside of the web request (store info in a db and get it later), there are many types of emails and I don't want to store the template data (user object, a few other LINQ objects) in a db to let it get rendered later. I could create a simpler, serializable POCO and save that in the db, but why? ... I just want rendered text!
I can create a new RedirectToAction object that checks if the headers have been sent (can't figure out how to do this -- try/catch?) and, if so, builds out a simple page with a meta redirect, a javascript redirect, and also a "click here" link.
Within my controller, I can remember if I've rendered an email and, if so, manually do #2 by displaying a view.
I can manually send the redirect headers before any potential email rendering. Then, rather than using the MVC infrastructure to redirecttoaction, I just call result.end. This seems easiest, but really messy.
Anything else?
EDIT: I've tried Dan's code (very similar to the code from Jan/Feb that I've already tried) and I'm still getting the same error. The only substantial difference I can see is that his example uses a view while I use a partial view. I'll try testing this later with a view.
Here's what I've got:
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;
}
CustomRenderers
public class CustomRenderers
{
public virtual string RenderViewToString3(ControllerContext controllerContext, string viewPath, string masterPath, ViewDataDictionary viewData, TempDataDictionary tempData)
{
//copy/paste of dan's code
}
}
Error
[HttpException (0x80004005): Cannot redirect after HTTP headers have been sent.]
System.Web.HttpResponse.Redirect(String url, Boolean endResponse) +8707691
Thanks,
James