Rick Strahl's Weblog  

Wind, waves, code and everything in between...
.NET • C# • Markdown • WPF • All Things Web
Contact   •   Articles   •   Products   •   Support   •   Advertise
Sponsored by:
Markdown Monster - The Markdown Editor for Windows

Migrating an ASP.NET app to IIS 7


:P
On this page:

I spent some time today to convert an ASP.NET app to run in Integrated Pipeline mode in IIS 7. The process was painless, but of course I wanted to check out some of the new integration features by applying some custom generated content to a static HTML file. In concept very simple, but as it turns out there are few tricks you need to use to manipulate the content correctly.

 

 

I set all of this up by taking an existing ASP.NET application and switching it into an Application Pool configured for Integrated Pipeline operation. The Integrated Pipeline essentially merges IIS functionality with ASP.NET’s pipeline so that HttpApplication events fire against every request that is directed at the current application or even the entire Web Server.

 

I've been using Vista Beta 2 for a couple of weeks now and have been doing all my Web development with IIS 7 and it works great. But I've been running my apps in ISAPI mode rather than the new Integrated Pipeline mode. Integrated Pipeline operation or ISAPI are the two modes supported and they are configured at the Application Pool level in IIS. Switching is easy - it's simply a matter of selecting the application pool and switching the pipeline mode in the basic settings.

 

Once switched over it’s possible that your app won’t run if you have any custom handlers or modules defined. Specifically you have to move out the <httpHandlers> and <httpModules> section of your Web.config into the a separate and new <system.webserver> general section:

 

<configuration>

  <system.webServer>

        <handlers>

            <add name="*.wws" path="*.wws" verb="*" type="Westwind.WebStore.PageRedirectorHTTPHandler" preCondition="integratedMode,runtimeVersionv2.0,bitness32" />

        </handlers>

      <modules>

        <add name= "SharewareModule" type="Westwind.WebStore.SharewareModule" preCondition="managedHandler"/>

      </modules>

        <validation validateIntegratedModeConfiguration="false" />

    </system.webServer>

</configuration>

 

Actually the above was auto-updated by using a command line call to:

 

%systemroot%\system32\inetsrv\APPCMD.EXE migrate config "West Wind Dev Site/wwstore"

 

When I originally switched my ASP.NET application’s Application Pool into Integrated mode I got a long and surprisingly detailed error message and the message directed me to the above command line to update my web.config file. Now these are error messages that are actually useful!

 

What this command does is upgrade your web.config by leaving the old handlers and module section intact and also adding the section above on the bottom. In Integrated mode IIS uses the lower section – if you switch back to ISAPI mode in the Pipeline the application still works with the old settings. Note however, that on older IIS versions the file will fail since the system.webServer section is not legal for Web.config.

 

With that in place both my handlers and modules run without any problems providing the same functionality as before in plain ASP.NET using the ISAPI or IIS 6 pipeline.

 

Modules are the fun part 

One of the new features of the integrated pipeline is that you can fire your module code against ANY request against the server at the level where the module is defined (in my case at the virtual level for an Application). This means I can apply module code not only against ASP.NET pages (anything that is mapped to the ASP.NET Handler) but against anything including say static HTML pages.

 

I have one very simple module in my application that pops up a demo message on the bottom of the page when the app is in compiled in demo mode. So I wanted to take advantage of these new features. I ran into some problems with this particular implementation and static content, but it actually provided some good insight in just how IIS 7 works in feeding all this content.

 

The module above is actually a little silly thing I use to brand the demo versions of the West Wind Web Store, which basically looks at the output and then appends a message to the bottom of the output (if it’s a text/html document that is). It sure would be nice if that also worked against a static page of which there are a few in the local application. Well, now I should be able to.

 

In operation against ASP.NET manged pages the module worked without any changes, so the original behavior worked just fine. The module is pretty trivial and it turns out that indeed IIS (not I say IIS not ASP.NET – think of system.webServer as IIS) fires the module just fine, but unfortunately I got no banner output for hitting a static HTML page.

 

Here’s the code:

 

private void application_PostRequestHandlerExecute(object sender, EventArgs e)

{

      HttpApplication app = (HttpApplication) sender;

     

      // *** Must make sure we don't add this to data responses!!!

      if (app.Context.Response.ContentType.ToLower() == "text/html")

            app.Context.Response.Write(SharewareMessage);

}

 

First shot above – doesn’t work. For a couple of reasons. The first is a simple HTTP issue. Static Pages are served by a built-in handler (StaticFile Handler) so the handler generates the output, creates headers etc. The static file handler adds a Content-Length header to the generated page, and my code that gets added to the page doesn’t affect this content length. Solution to that particular problem:

 

private void application_PostRequestHandlerExecute(object sender, EventArgs e)

{

    HttpApplication app = (HttpApplication) sender;

     

    // *** Must make sure we don't add this to data responses!!!

    if (app.Context.Response.ContentType.ToLower() == "text/html")

    {

        app.Context.Response.Headers.Remove("Content-Length");

        app.Context.Response.Write(SharewareMessage);

    }

}

 

Remove the Content-Length which removes the originally generated content-length and causes IIS to write the length when output generation is completed.

 

Running the code with this fix shows that IIS is indeed generating the right content length now, but unfortunately the added text is still not displaying. Looking at the generated HTTP headers shows why:

 

HTTP/1.1 200 OK

Content-Type: text/html

Content-Encoding: gzip

Last-Modified: Sun, 18 Jun 2006 06:23:28 GMT

Accept-Ranges: bytes

ETag: "07025b69f92c61:0"

Vary: Accept-Encoding

Server: Microsoft-IIS/7.0

X-Powered-By: ASP.NET

Date: Sun, 18 Jun 2006 06:57:53 GMT

Content-Length: 889

 

Note the GZIP compression. IIS 7 has a static file compression module that is active by default, so my HTML page gets GZIP’d as part of the HTTP Handler operation that ‘creates’ the page output. Once I add my non-GZipped string to this output this output is ignored. The client reads the GZip header and from that header gets the content size which won’t include my generated output.

 

The brute force solution is to remove the StaticCompression Module:

<system.webServer>

    <modules>

      <remove name="StaticCompressionModule" />

      <add name= "SharewareModule" type="Westwind.WebStore.SharewareModule" />

    </modules>

      <validation validateIntegratedModeConfiguration="false" />

</system.webServer>

 

This is truly a brute force approach and there might be a better way to filter this. GZip compression is certainly a useful feature and is applied to images, style sheets etc. so turning it off for all of these just for getting this mechanism to work is a bit much. But at the moment I can’t see how to do that. Notice that these changes are made at the virtual level’s web.config file so they apply only to my Application, not to the whole server (although you can set this up to run at the Web Root or the whole Web Server as well!)

 

I’m thinking that it would be nice if Modules in general had some sort of filter that allowed to specify what file types they are applied to. In most cases this is not necessary as modules can internally decide what content to run against. But in some scenarios where complex modules like the Compression Module runs it might be nice to exercise some fine grained control and just tell the module to only execute against a certain path or not execute against a certain path structure.

 

 

It’s going to take getting used to all this new functionality, but it’s pretty exciting to see this integration and have it work so smoothly using existing concepts that we're familiar with in ASP.NET. The really cool thing about this is that you can write HttpHandlers and HttpModules that fully participate in the IIS pipeline and can run against ANY request, not just ASP.NET requests.

 

Good stuff.


The Voices of Reason


 

Jason Haley
June 18, 2006

# Interesting Finds: June 18, 2006 AM edition


Steve from Pleasant Hill
June 18, 2006

# re: Migrating an ASP.NET app to IIS 7

Having followed your efforts and pain getting Vista up and running (which you have graciously documented) it seems like running things in IIS 7 was one of your primary goals. Congratulations!

Looking forward to more IIS 7 posts.

scottgu
June 18, 2006

# re: Migrating an ASP.NET app to IIS 7

Hi Rick,

We actually registered the <system.webServer> section with ASP.NET 2.0. On non-IIS7 systems it maps to an ignore sectionhandler -- specifically so that having it in your web.config file won't cause any errors.

This means you can map an app to run in both integrated mode on IIS7 (and take advantage of the new module features) and on IIS6 without having to make any source changes.

Hope this helps,

Scott

Rick Strahl
June 18, 2006

# re: Migrating an ASP.NET app to IIS 7

Thanks Scott for the clarfication. I had tried this before in my previous attempts at getting IIS 7 to work on VPC and copied back a web.config file and that failed, but it's quite possible that there was something else screwy in the config.

Nice touch to be thinking ahead in ASP.NET 2.0 for things to come!




clark
June 19, 2006

# re: Migrating an ASP.NET app to IIS 7

For more reading, I think that Dino Esposito talks about this in his book: ASP.NET 2.0 Applications Advanced Topics: Chapter 2 HTTP Handlers and Modules. I'd be interested to hear what you think about his book.

Rick Strahl
June 19, 2006

# re: Migrating an ASP.NET app to IIS 7

Hmmm... I just read that chapter <g>, but I don't remember offhand discussion of this...

The book is pretty good and very deep in a number of advanced topics whcih is great, but I'm a bit disappointed in the way this series turned out. The Core and Pro book are really separate and the Core book is really light especially for a Dino book. The Pro book has good stuff but it's kind of mix and match on what it covers. It's excellent at what it covers, but it's not a complete ASP.NET book.

I would have much preferred one monster tomb with the whole of ASP.NET addressed in a more consistent manner than an 'intro' and 'advanced' book which makes both of them feel incomplete individually and even in combination.

Still the Pro book is worth having as a reference and worth reading for what it does cover!


clark
June 20, 2006

# re: Migrating an ASP.NET app to IIS 7

I can agree with that. I feel that at times, Dino skips around and then comes back to topics. But that is just me being overly critical...I'm a big Dino fan.

I think that he is doing the same approach (intro and advanced) for Atlas. Which he is waiting to release...but you probably already know that.

thanks for the discussion, I appreciate it.

Andrey Skvortsov
October 17, 2006

# re: Migrating an ASP.NET app to IIS 7

One question-why you can't add compression module after you module?

Thanks.

Rick Strahl
October 17, 2006

# re: Migrating an ASP.NET app to IIS 7

It doesn't make a difference where you load the module Andrey. What matters is which events are hooked and which event fires first. The problem in the example is that the page content is modified after the compression has occurred which will fail because you now have GZip content with an HTML message appended.

There are other ways to acomplish this such as capturing the page output for example, or using a filter, but it's not quite as easy to do <s>.

Andrey Skvortsov
October 17, 2006

# re: Migrating an ASP.NET app to IIS 7

Thanks for clarification.But it MUST be a difference IMHO first module hooked event-first process it(module processing order must have concrete meaning),but I can't reorder modules in RC2-not native for now,it seems...
I tried <remove name="StaticCompressionModule" /><add name="mymod"/><add name="StaticCompressionModule"/> but remove takes precedence or compression module can't be added on app level or...I don't know but compression dosen't work at all.

Rick Strahl
October 17, 2006

# re: Migrating an ASP.NET app to IIS 7

Andrey, Module ordering will only affect modules that are hooking the same events. Otherwise the ordering is determined by the ASP.NET pipeline and the order of the events that are hooked by each module.

You can't change the fact that BeginRequest fires before EndRequest no matter how you rearrange your modules. <s>

# DotNetSlackers: Migrating an ASP.NET app to IIS 7


Thomas E Rozzi
February 15, 2007

# re: Migrating an ASP.NET app to IIS 7

I get an error 13 Bad Data when trying to start II7 visa vi Windows Process Application Service. Any guidance???

Thanks
t

Steve Schofield's Blog
July 06, 2007

# Steve Schofield's Blog : IIS7 compression info

I just posted a blog about IIS6 compression. I was wondering if IIS7 compression was much different. After some searching, here are some links I found regarding IIS7 compression. I enabled this successfully on my IISLogs.com server and is working great.

Advanced Technology
February 29, 2008

# Testare un HttpHandler in ClassicMode

Testare un HttpHandler in ClassicMode

ilpostino
September 15, 2008

# re: Migrating an ASP.NET app to IIS 7

Hi,

I have a webapplication on IIS7, its a proxy server, so no static files, I have activated the compression on DynamicData, I am sure that i did it, coz I did the same thing for a normal Webapplication that it contains a simple site and it works, but for the proxy application it doesnt work. To now if it works I use NetworkMonitor and see the response of the server if type is GZIP or I see the response if it contains HTML data or unreaded Data

any one have any idea what could be the reason?

Thanks

Juri
July 17, 2012

# re: Migrating an ASP.NET app to IIS 7

I'm currently in the process of migrating our ASP.net WebForms apps from an IIS6 server (running classic mode) to an IIS7.5 integrated mode.

Didn't you have any issues with WebResource.axd files? I'm continuously getting an error message stating:
System.NullReferenceException: Object reference not set to an instance of an object.
   at System.Web.HttpContext.RequestRequiresAuthorization()
   at System.Web.Caching.OutputCacheModule.OnLeave(Object source, EventArgs eventArgs)
   at System.Web.HttpApplication.SyncEventExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute()
   at System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously)


I solved the same issue for the ScriptResource.axd files by re-registering them explicitly in the handlers section like

<handlers>
    <remove name="ScriptResource"/>
    <add name="ScriptResource" preCondition="integrated" verb="GET,HEAD" path="ScriptResource.axd" />
</handlers>


Any idea what could cause the problem??
Any

West Wind  © Rick Strahl, West Wind Technologies, 2005 - 2024