3

Is there an equivalent to Server.Execute() in ASP.NET MVC?

Apparently that only works with traditional webforms .aspx pages.

Update: I need to grab the rendered HTML from a different action in the same controller to generate a PDF. Maybe there's a way to execute a View without outputting the html to the response stream?

Jason Berkan
  • 8,734
  • 7
  • 29
  • 39
  • Sorry, I just found out my solution won't work with the default view engine. –  Oct 12 '09 at 20:45
  • Looked some more and it still exists within the context, which you can access from within the controller (`this.Context.Server.Execute(/**/)`). Not sure if that will work with a ViewPage, however. –  Oct 12 '09 at 20:54
  • Will, just tried your suggestion. The server object in ControllerContext.Server is the same Server object underneath the covers as the one in webforms/.aspx pages. –  Oct 12 '09 at 22:04

4 Answers4

2

Look at this solution:

Render a view as a string

I used it to generate partial view and it worked. You'll have to switch to partial, but it shouldn't be the problem.

Edit:

I've done some corrects, worked with reflector and used solutions from previous questions. This code looks nicer. View rendering engine is strongly connected to HttpContext.Current, so we have to do some hacking:

    /// <summary>Renders a view to string.</summary>
    public static string RenderViewToString(this Controller controller,
                                            string viewName, object viewData)
    {
        //Getting current response
        var response = HttpContext.Current.Response;
        //Flushing
        response.Flush();

        //Finding rendered view
        var view = ViewEngines.Engines.FindPartialView(controller.ControllerContext, viewName).View;
        //Creating view context
        var viewContext = new ViewContext(controller.ControllerContext, view,
                                          controller.ViewData, controller.TempData);

        //Since RenderView goes straight to HttpContext.Current, we have to filter and cut out our view
        var oldFilter = response.Filter;
        Stream filter = new MemoryStream(); ;
        try
        {
            response.Filter = filter;
            viewContext.View.Render(viewContext, null);
            response.Flush();
            filter.Position = 0;
            var reader = new StreamReader(filter, response.ContentEncoding);
            return reader.ReadToEnd();
        }
        finally
        {
            filter.Dispose();
            response.Filter = oldFilter;
        } 
    }

It should be easily convertible to allow to render View (change ViewEngines.Engines.FindPartialView to ViewEngines.Engines.FindView). I don't see better solution.

Community
  • 1
  • 1
LukLed
  • 31,452
  • 17
  • 82
  • 107
  • Thanks. This works, BUT, I just can't believe that we have to jump through so many hoops just to get something like this going. It seems like a big hack to me. –  Oct 13 '09 at 20:06
  • What about the case when we don't have the Controller, problem is when I want to call Server.Execute from a library having no access to HttpContext, it is still possible. – Akash Kava Oct 15 '13 at 13:45
0

The equivalent to Server.Execute in MVC is to simply instantiate the desired controller and execute the action method.

public ActionResult EntryAction()
{
    ProductController pc = new ProductController();

    return pc.Index();
}

Of course, you can skip the first step if the desired action is on the same controller.

Richard Szalay
  • 83,269
  • 19
  • 178
  • 237
  • Andrew actually needs to capture the Html from the view to write to a PDF document. –  Oct 12 '09 at 20:46
0

Many thanks for your RenderViewToString code snippet. I had to make some changes and I integrated it into to our base controller, but it works really well.

    /// <summary>Renders a view to string.</summary>
    public string RenderViewToString(ViewResult viewResult)
    {
        //Getting current response
        //var response = HttpContext.Current.Response;
        var response = Response;
        //Flushing
        response.Flush();

        //Finding rendered view
        var view = ViewEngines.Engines.FindView(ControllerContext, viewResult.ViewName, viewResult.MasterName).View;

        //Creating view context
        var viewContext = new ViewContext(ControllerContext, view,
                                          ViewData, TempData);

        //Since RenderView goes straight to HttpContext.Current, we have to filter and cut out our view
        var oldFilter = response.Filter;
        Stream filter = new MemoryStream();
        try
        {
            response.Filter = filter;
            viewContext.View.Render(viewContext, null);
            response.Flush();
            filter.Position = 0;
            var reader = new StreamReader(filter, response.ContentEncoding);
            return reader.ReadToEnd();
        }
        finally
        {
            filter.Dispose();
            response.Filter = oldFilter;
        }
    }
Bart McLeod
  • 151
  • 2
  • 6
0

I have implemented this in MVC way, It work with all the result types. Including file download.

I created a custom ActionResult ExecuteUrlResult, and created two Helper Methods in my BaseController.

ExecuteUrlResult

public class ExecuteUrlResult : ActionResult
{
    public string Url { get; protected set; }
    public bool PreserveForm { get; protected set; }

    public ExecuteUrlResult(string url)
    {
        this.Url = url;
    }
    public ExecuteUrlResult(string url, bool preserveForm)
    {
        this.Url = url;
        this.PreserveForm = preserveForm;
    }

    public override void ExecuteResult(ControllerContext context)
    {
        context.HttpContext.Server.Execute(this.Url, this.PreserveForm);
    }
}

Helper Methods

    protected internal ExecuteUrlResult ExecuteUrl(string url)
    {
        return new ExecuteUrlResult(url);
    }
    protected internal ExecuteUrlResult ExecuteUrl(string url, bool preserveForm)
    {
        return new ExecuteUrlResult(url, preserveForm);
    }