Migrating an ASP.NET app to IIS 7
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.
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
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.
Other Posts you might also like
- Adding minimal OWIN Identity Authentication to an Existing ASP.NET MVC Application
- Map Physical Paths with an HttpContext.MapPath() Extension Method in ASP.NET
- Getting the Client IP Address in ASP.NET Core
- Resolving Paths To Server Relative Paths in .NET Code
- Preventing iOS Textbox Auto Zooming and ViewPort Sizing
The Voices of Reason
# re: Migrating an ASP.NET app to IIS 7
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
# re: Migrating an ASP.NET app to IIS 7
Nice touch to be thinking ahead in ASP.NET 2.0 for things to come!
# re: Migrating an ASP.NET app to IIS 7
# re: Migrating an ASP.NET app to IIS 7
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!
# re: Migrating an ASP.NET app to IIS 7
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.
# re: Migrating an ASP.NET app to IIS 7
Thanks.
# re: Migrating an ASP.NET app to IIS 7
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>.
# re: Migrating an ASP.NET app to IIS 7
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.
# re: Migrating an ASP.NET app to IIS 7
You can't change the fact that BeginRequest fires before EndRequest no matter how you rearrange your modules. <s>
# re: Migrating an ASP.NET app to IIS 7
Thanks
t
# re: Migrating an ASP.NET app to IIS 7
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
# re: Migrating an ASP.NET app to IIS 7
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
# re: Migrating an ASP.NET app to IIS 7
Looking forward to more IIS 7 posts.