Yesterday I wrote about a technique for capturing the HTML output by a view so that you could do something else with it, like send it in an email. However there turned out to be serious problems with that technique. Because it used the current HttpResponse instance it set the internal _headersWritten flag of the Response. When I subsequently wanted to execute a redirect I got an error saying:

"System.Web.HttpException: Cannot redirect after HTTP headers have been sent."

I tried to find some way to reset this flag, but there didn't seem to be any way of doing it. However, after digging around the MVC source code and with a bit of help from Reflector I found an entirely different (and better I think) approach. Rather than use the MVCContribBlockRenderer I simply replace the current HttpContext with a new one that hosts a Response that writes to a StringWriter.

I've wrapped it all up in an extension method on Controller called CaptureActionHtml, so you can write code like this example from Suteki Shop that I use to send an order email confirmation:

There are also overloads that allow you to call an action on a different controller than the current one, here's an example from a test controller I wrote where the TestController is executing the Item action on the orderController, using an alternate master page, called "Print", and then writing the HTML output to a log:

The best way of seeing this all in action is to get the latest code for Suteki Shop, There's a stand-alone Suteki.Common assembly that you can include in your project that provides these extension methods in the Suteki.Common.Extensions namespace.

But here is the code for the extension class if you want to just cut and paste:

Update: I found that this technique has problems because it changes the state of the HttpResponse by setting its internal _headersWritten flag which means you can't subsequently execute a redirect. I've developed a different and better technique which involves replacing the current HttpContext. See this post:

Sometimes it's useful to be able to capture the output of a view rather than simply have it streamed to the Response.OutputStream. A couple of things have made this quite easy to do. Firstly, from CTP 3 of the MVC Framework, controllers now return an ActionResult rather than just setting properties and leaving the infrastructure to act on them. This means we can take a ViewResult and call ExecuteResult on it before it's returned, allowing us to take control of the rendering.

The second innovation is a nice piece of work from the MVCContrib project. This is an open source add-on to the MVC Framework that provides lots of useful stuff like standard error handling, data binding, IoC container integration and lots more. If you're building MVC Framework applications you really should look at what this project can do for you. Deep in its bowels is the BlockRenderer class, this can take any action that renders to Response.OutputStream and divert it to a string. It does it by adding a custom filter to the Response filter chain.

Here's a silly example of it in action. I've created a test controller and in its Index action I'm calling the Item action on another controller called orderController. I'm then using the BlockRenderer to capture the rendering of the Item view in a string, which is then simply displayed.

Note that I change the name of the master page. You can imagine that the "Print" master page is a simple body tag container without any of the site furniture: menus and wotnot, that my standard master page might have. I also have to change the controller route value because the ViewLocator class of the MVC Framework uses it to find the view name.

The main reason I have for doing this, is so that I can send a view by email, but you can imagine how it might be useful to do other things like build up a page from multiple controllers and or views.

DDD7 (UK) has recently been announced. It's going to be held at the Microsoft campus in Reading on the 22nd of November. Submissions for talks have now opened. I've put forward two suggestions, both on IoC containers. The first will be an updated version of the IoC introductory talk I gave last year:

Why do I need an Inversion of Control Container

"Inversion of Control (IoC) containers such as Unity Windsor and Structure Map are a hot topic in the Microsoft Development world. What are they and why is there such a buzz about them? How will they help me build better applications? Join me on a trip to an parallel universe of application architecture where I show how IoC containers at last make component oriented software development a reality."

This is an introduction to IoC and IoC containers. It's a description of how the IoC design pattern is a simple way of building component oriented software and how IoC containers add a bit of magic to that party. The other talk I've suggested is a deeper look at the subject by examining its use in a real application:

Using an Inversion of Control Container in a real world application

"Going beyond an initial introduction to IoC containers, this talk shows their use in an open source eCommerce application, Suteki Shop. I will show how the IoC container helps us write component oriented software and can significantly simplify both our architecture and code. This will include a look at some nice techniques such as generic repositories, using IoC containers with the MVC Framework and how they can help us host services."

The idea here is to pass on my experience of building applications based around these techniques. There will be a lot of code and practical advice. I'm still on a steep learning curve myself with all this, so I expect that by November there will be a lot in this talk that I'm not even aware of yet.

Code Rant

Notepad, thoughts out loud, learning in public, misunderstandings, mistakes. undiluted opinions. I'm Mike Hadlow, an itinerant developer. I live (and try to work in) Brighton on the south coast of England.

All code is published under an MIT licence. You are free to take it and use it for any purpose without attribution. There is no warranty whatsoever.