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:
West Wind WebSurge - Rest Client and Http Load Testing for Windows

IIS Root Web and web.config Inheritance - grrrrrr...


:P
On this page:

I’m spending sometime this evening doing some maintenance on my main Web site. My main site – the static part of it anyway – uses only minimal ASP.NET code. Most of the site still runs classic ASP pages because there’s very little dynamic stuff going on on the home page or the various product and informational pages.

 

As of recent though I’ve been slowly adding more and more functionality in various places. Tonight I built a small handler that lets all download files that are available in EXE format be automatically available as a Zip file so that those folks that get blocked from downloading EXE files can get zipped files to download safely (a silly rule if you ask me – but…)

 

The module was trivial to build – I used #SharpZip for the file compression to write out a zip file to disk and the module then compares timestamps to only zip the file once and when the file has changed always serving the file with TransmitFile. Piece of cake. To build the module I created a temporary directory/site and worked off that. All’s well there.

 

Unfortunately I’ve not been able to install this module on my full site though. My plan was to install the module in the root Web site and hook it into the Web.config of the root site. So I added my assembly and the SharpZip assembly into my root site’s BIN directory. Fired up my local root Web site and tested the zip file creation – all’s working on that end.

 

But… and there’s always a but – adding this module to the root site fucks up every single one of my sub sites! So my WebLog and West Wind Web Store which are virtual directories defined below the Web Root (localhost/weblog, localhost/webstore etc.). Each is in a separate path off the root.

 

The problem is that the Web.config is inherited from the root web down into the virtual applications and I get the following error in my sub-application (the WebLog in this case)

 

Description: An error occurred during the processing of a configuration file required to service this request. Please review the specific error details below and modify your configuration file appropriately.

Parser Error Message: Could not load file or assembly 'WestwindWebSiteComponents' or one of its dependencies. The system cannot find the file specified. (C:\westwind\web.config line 40)

Source Error:

 

Line 38:         -->

Line 39:                  <httpModules>

Line 40:                           <add type="WestwindWebSiteComponents.ExeZipModule,WestwindWebSiteComponents" name="ExeZipModule" />

Line 41:                  </httpModules>

Line 42:         </system.web>


Source File: C:\westwind\web.config    Line: 40

 

The error is telling me that the module doesn’t exist in the Weblog application. So the sub applications are inheriting the root application’s web.config always.

 

So far, I’ve been able to get away with running without a Web.config altogether so this has never come up. What sucks about this this handler/module loading is that the root web.config is used, but it’s not smart enough to actually use the root directory for finding the module and instead trying to look for it in the lower level application…

 

There are a workarounds, but none of them are pretty. I can GAC my component and SharpZipComponent and in that case each of the sub applications automatically loads this module. I don’t really consider this a great option, because the sub applications shouldn’t have to load this module!

 

I can go into each of the Web.config files of the sub applications and add a remove tag to the httpModules section. This is a bit painful, because there are around 25 apps running on this server and updating each one of these is not really an option.

 

In the end the solution I used was to use Global.asax and HttpApplication.Init() to hook the handler in code:

 

    public WestwindWebSiteComponents.ExeZipModule ExeZip;

   

    void Application_Start(object sender, EventArgs e)

    {

    }

 

    public override void Init()

    {

        base.Init();

 

        ExeZip = new WestwindWebSiteComponents.ExeZipModule();

        ExeZip.Init(this);

    }

 

 

And now I got a working module that fires only on the root Web site.

 

It’s an easy and relatively clean solution once you get over the surprise of the inheritance structure of the site.

 

I still think that the hierarchical nature of the Web Root doesn’t do what I would expect. When I think of the Web Root I think of it as just another virtual directory/application. To have the settings of the root affect all sub virtuals can cause all sorts of issues.

 

Moral of the story for me? Keep the web.config of the root clean.

 

FWIW if anybody’s interested here’s the code for the Exe->Zip Module.

 

/// <summary>

/// Module that pops automatically creates .Zip files from

/// Exe files and stores them in the local file system. Checks

/// for date stamps and creates new zip files only if the EXE

/// file is newer than the

/// </summary>

public class ExeZipModule : IHttpModule

{

    object SyncLock = new object();

 

    public void Dispose()

    {

    }

 

    public void Init(HttpApplication context)

    {

        context.BeginRequest += new EventHandler(context_BeginRequest);

    }

 

    public void context_BeginRequest(object sender, EventArgs e)

    {

 

        System.Web.HttpApplication App = (System.Web.HttpApplication)sender;

 

        string Path = App.Request.Path.ToLower();

 

        if ( !Path.EndsWith(".exe.zip") )

            return;

 

        string ZipFilePath = App.Server.MapPath(Path);

        string FilePath = ZipFilePath.Replace(".zip", "");

 

       

        FileInfo fiFile = new FileInfo(FilePath);

        FileInfo fiZip = new FileInfo(ZipFilePath);

 

        bool FileExists = File.Exists(FilePath);

        bool ZipExists = File.Exists(ZipFilePath);

       

        if (!FileExists)

        {

            App.Response.StatusCode =400;

            return ;

        }

 

        // If the file already exists we need to

        // check if the exe is newer

        if (ZipExists  && FileExists )

        {

            if (fiFile.CreationTimeUtc <= fiZip.CreationTimeUtc)

            {

                // File already exists and is not newer - just return to client

                App.Response.TransmitFile(ZipFilePath);

                App.Response.End();

                return;

            }

        }

 

        // *** Protect section for dupe access

        lock (SyncLock)

        {

            // *** Double check file creation

            if (!File.Exists(ZipFilePath))

            {

                try

                {

                    using (ZipFile zf = ZipFile.Create(ZipFilePath))

                    {

                        zf.BeginUpdate();

                        zf.Add(FilePath);

                        zf.CommitUpdate();

                        zf.Close();

                    }

                }

                catch (Exception ex)

                {

                    if (File.Exists(ZipFilePath))

                        File.Delete(ZipFilePath);

 

                    App.Response.StatusCode = 404;

                    App.Response.End();

                    return;

                }

            }

 

            App.Response.TransmitFile(ZipFilePath);

            App.Response.End();

        }

    }

 

}

 

Note if you plan to use this (or something similar) you’ll need to map the .zip extension to the ASP.NET ISAPI Dll and make sure that the NETWORK SERVICE or ASPNET account has rights to write files into the download directory.

Posted in ASP.NET  

The Voices of Reason


 

Marc Brooks
January 14, 2007

# re: IIS Root Web and web.config Inheritance - grrrrrr...

Couldn't you simply do a <remove> of the module on the nested sites in thier web.config?

Rick Strahl
January 14, 2007

# re: IIS Root Web and web.config Inheritance - grrrrrr...

Yeah I could except but as I mentioned above I have close to 25 sub-virtuals that run ASPX code. I really don't feel like going through all of them everytime I add a module <s>... and try to remember which module is failing <s>.

Betty
January 14, 2007

# re: IIS Root Web and web.config Inheritance - grrrrrr...


Rick Strahl
January 14, 2007

# re: IIS Root Web and web.config Inheritance - grrrrrr...

Thanks Betty - Yes Steve's post lets me work around this issue with the following in web.config:

  <location  inheritInChildApplications="false">
    <system.web>
    <httpModules>
            <add type="WestwindWebSiteComponents.ExeZipModule,WestwindWebSiteComponents"  name="ExeZipModule" />
        </httpModules>
    </system.web>
  </location>

Nick
January 25, 2007

# re: IIS Root Web and web.config Inheritance - grrrrrr...

Intersting article Rick... thanks for the information.

I am experiencing something like the opposite. I have ONE web application, with several folders in its file structure. I then have a library with a custom httpmodule. What I would like is to load this httpmodule ONLY in a specific sub-folder. So I have added a web.config to that directory, and overridden the <httpmodules> tag.

However, the module simply won't get called. It isn't generating any errors, it simply isn't loading. Furthermore, I know that the httpmodule works, because if I add the type to the web.config at the ROOT of the site, it works fine.

A lot of frustration! If you have any ideas I'd appreciate it.

Thanks, Nick.

Nick
January 25, 2007

# re: IIS Root Web and web.config Inheritance - grrrrrr...

Just an update... more searching with Google turned up the following:

http://lab.msdn.microsoft.com/productfeedback/viewfeedback.aspx?feedbackid=91763c85-649a-4bcd-8fbe-11f4c1d14fc5

So it currently looks like this is a bug, and not likely to be resolved in the near future.

If anyone has a workaround, I would be SO thankful :-)

Nick.

Sam
February 05, 2007

# re: IIS Root Web and web.config Inheritance - grrrrrr...

Try this, I was looking at the same problem and came by this


"There’s a good solution for this in ASP.NET v2.0 now. At least if you don’t need the assembly in the vdir. The <location> tag has a new property called inheritInChildApplications. All you need to do is wrap your modules and assemblies in that location tag and it won’t inherit in the vdirs"

i.e.

<location inheritInChildApplications=”false”>
<system.web>
<httpModules>
….
</httpModules>
</system.web>
</location>

found @ http://aspadvice.com/blogs/ssmith/archive/2006/08/21/HttpModule-Breaks-SubApplications-Problem-Solved.aspx

Rick Strahl
February 05, 2007

# re: IIS Root Web and web.config Inheritance - grrrrrr...

Right - I found that earlier (if you look back at the comments) from Steve's post <s>...

Cameron
March 02, 2007

# re: IIS Root Web and web.config Inheritance - grrrrrr...

So the other attribute of the location tag for the web.config is the path attribute. If you wrap your xml that is screwing all the other sites in a location tag you should be good.


<location path="SubDir1">
<system.web>
<httpModules>
<add type="WestwindWebSiteComponents.ExeZipModule,WestwindWebSiteComponents" name="ExeZipModule" />
</httpModules>
</system.web>
</location>


This will only make the settings apply to the one directory. I think this is just what you're looking for.

I, on the other hand, am looking for the opposite. I want to make some web.config settings apply to ALL subfolders and Virtual Directories EXCEPT one. I can't think of how to do it without duplicating the location tag and web.config code 19 times, if I had 20 directories, and just leave out the one I don't want the settings to apply to.

Thoughts?

tony
July 18, 2007

# re: IIS Root Web and web.config Inheritance - grrrrrr...


Scott
September 14, 2007

# re: IIS Root Web and web.config Inheritance - grrrrrr...

This method seems to be the only method that works when you have .net 1.1 application nested with in the .net 2.0 application.

The .net 1.1 will inherit the location setting and complain about the inheritInChildApplications not being a valid attribute.

Thank you for the solution.

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