Bundling not working for mapped virtual directory - iis-7

We have a site where CSS and JavaScript are bundled into /Content/ and /Scripts/ directories respectively. Both of these are also mapped targets of virtual directories in a separate domain, which is used for static resources (so cookies aren't served).
The problem is that in our production environment, where compilation -> debug is set to false (so bundling & minification is enabled), the relative URLs work fine and become served from the bundling system, but the static domain URLs (using the virtual directories pointing to the same location) yield HTTP 404 errors.
Is there a way to configure IIS to allow the bundling to work for the contents of this virtual directory? I have visions of having to create a separate dummy MVC project for the static domain so that it recognizes the bundling, but would like to see if there's a better solution.
Here's what the current processing order from IIS seems to be:
Receive incoming request for (bundled) resource, e.g. [static domain]/Content/all.css
Run any HTTP handlers, e.g. bundling if available (none in this case of the static domain)
Follow the virtual directory and serve the resource if available, i.e. searches for ([main domain]/Content/all.css)
Because this file doesn't really exist on the file system, an HTTP 404 is raised. Ideally, step 2 should be running after step 3.
Many thanks.

I've done a bit of experimentation and managed to solve it. What doesn't work is pointing the root of the static site to the same root of the main website, as the web.config files are shared - any change (i.e. disabling session state) made to the static site is also applied to the main website.
In the end, what works is the following:
Have the static domain point to a separate root folder on the file system.
Keep the virtual directories on the static site (/Content/ and /Scripts/) pointing to their corresponding locations on the main site.
Since we use Helicon's URL-rewriter for cache-busting, create a bare-bones .htaccess file with the same rule as is used in the main site; put that in the root of the static site.
Copy the global.asax file from the main website into the root of the static site. This cannot be a shortcut.
Copy the contents of the /bin/ folder from the main website into the static site.
A virtual directory in IIS pointing to the /bin/ folder on the main website will NOT work.
Creating a shortcut in the file system to the /bin/ folder on the main website also will not work.
In IIS, ensure the static site uses its own application pool, and that it's configured for .NET 4.0, Integrated pipeline mode. This is so that the MVC 4 bundling will work.
Create a bare-bones web.config with the UrlRoutingModule added.
This is our web.config for the static site:
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<system.web>
<customErrors mode="Off">
</customErrors>
<sessionState mode="Off" />
<pages enableSessionState="false" enableViewState="false" enableViewStateMac="false" renderAllHiddenFieldsAtTopOfForm="false" />
</system.web>
<system.webServer>
<validation validateIntegratedModeConfiguration="false" />
<modules runAllManagedModulesForAllRequests="true">
<remove name="ScriptModule" />
<add name="UrlRoutingModule" type="System.Web.Routing.UrlRoutingModule, System.Web.Routing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
</modules>
<urlCompression doStaticCompression="true" doDynamicCompression="true" />
<security>
<requestFiltering allowDoubleEscaping="True" />
</security>
<tracing>
<traceFailedRequests>
<add path="*">
<traceAreas>
<add provider="ASPNET" areas="Infrastructure,Module,Page,AppServices" verbosity="Verbose" />
</traceAreas>
<failureDefinitions timeTaken="00:00:00" statusCodes="200" />
</add>
</traceFailedRequests>
</tracing>
<!-- Cache static content for a month, only enable on UAT or Live -->
<staticContent>
<clientCache cacheControlMode="UseMaxAge" cacheControlMaxAge="30.00:00:00"/>
</staticContent>
</system.webServer>
</configuration>
The main idea is that bundling requires ASP.NET MVC4 to be enabled and running on the static site, since the bundling is evaluated by IIS before the virtual directories are considered (and there doesn't seem a way to reverse that).

In css files create urls with relative paths, eg:
div.loader {
background-image: url("../grail/images/loader.gif");
}
In js files use hidden file from masterpage:
#Html.Hidden("HiddenCurrentUrl", Url.Content("~"))
In bundles instead of CssRewriteUrlTransform use your custom IItemTransform
public class VirtualCssRewriteUrlTransform : IItemTransform
{
private CssRewriteUrlTransform wrapper;
public VirtualCssRewriteUrlTransform ()
{
this.wrapper = new CssRewriteUrlTransform();
}
public string Process(string includedVirtualPath, string input)
{
var result = this.wrapper.Process(includedVirtualPath, input);
// if virtual directory exists
if (HttpRuntime.AppDomainAppVirtualPath != "/")
{
result = result.Replace(#"url(/", #"url(" + HttpRuntime.AppDomainAppVirtualPath + #"/");
}
return result;
}
}

Related

How to register an IHttpHandler as a default document

I have an IHttpHandler registered in web.config like so:
<system.webServer>
<validation validateIntegratedModeConfiguration="false" />
<!-- These are the HTTP handler registrations for IIS7+ in Integrated Pipeline mode; that is the current recommended mode for all deployments. -->
<handlers>
<add name="LaunchHandler" verb="GET,POST" path="launch.axd" preCondition="integratedMode" type="Foo.LaunchRequestHandler, Foo" />
...
</handlers>
</system.webServer>
This works fine if I navigate to /launch.axd directly, but I want this handler to run for the root path.
My initial approach was to create a default.aspx page that redirects to launch.axd, this does work but obviously requires an extra roundtrip to the browser.
As an alternative to using a default.aspx page I tried adding launch.axd as a Default Document, like so:
<defaultDocument>
<files>
<add value="launch.axd"/>
...
</files>
</defaultDocument>
However this does not work. Navigating to to root path returns this message:
HTTP Error 403.14 - Forbidden
The Web server is configured to not list the contents of this directory.
Most likely causes:
A default document is not configured for the requested URL, and directory browsing is not enabled on the server.
I.e. the request is being handled by DirectoryListingModule.
Any ideas? Can a handler that is not backed by a file (aspx, ashx) be registered as a default document?

Creating a folder with the same name as a controller results in 404 - Web API 2

If one creates a root level folder with the same name/route as a controller IIS seems to try and route to the folder and not consult the WebAPI 2 routing.
Example:
ProjectA
|- Controllers/
|-- ExamplesController.cs
|- Examples/
ExamplesController
[RoutePrefix("examples")]
public class ExamplesController : ApiController
{
[HttpGet]
[Route("")]
public async Task<IHttpActionResult> Get()
{
// code
}
}
The expectation is that hit the controller, however IIS seems to try and browse the folder instead.
Example: http://localhost:123/examples
Attempted Solutions:
I've tried to play around the IIS Handlers to see if I can remove all other handlers but that didn't help
Web.config
<system.webServer>
<modules>
<remove name="WebDAVModule" />
</modules>
<handlers>
<clear/>
<add name="ExtensionlessUrlHandler-Integrated-4.0" path="*." verb="*" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0" />
</handlers>
</system.webServer>
The routing system checks the file system to see if a URL matches a file/folder on the disk. If it finds a match, the routing is ignored and the request bypassed any route entries so that the file will be served directly. This is there so that static files are served without going through MVC Routing
To change this behavior you can set the RouteExistingFiles property to true in your RouteConfig, however this may impact your static files, so do test them thoroughly
routes.RouteExistingFiles = true;

Trying to secure all aspx files in a folder secure by IP address

I like to secure all aspx files in a folder ~/Secure/ secure such that specific IP addresses can access the folder's aspx files. I added the following web.config file to the folder, hoping that it adds to the parent web.config:
<configuration>
<system.webServer>
<security>
<ipSecurity allowUnlisted="false">
<clear/>
<add ipAddress="192.168.100.1" />
<add ipAddress="169.254.0.0" subnetMask="255.255.0.0" />
</ipSecurity>
</security>
</system.webServer>
</configuration>
The problem is that I get this error when I try to access to any of the aspx pages in the folder:
This configuration section cannot be used at this path. This happens when the section is locked at a parent level. Locking is either by default (overrideModeDefault="Deny"), or set explicitly by a location tag with overrideMode="Deny" or the legacy allowOverride="false".
What does it take to make this idea happen? I like to just include one web.config file to a folder and that enforces the IP address authorization. I like this idea, since it is no-code and config only.
You cannot do it in the website web.config only.
If you can use IIS manager:
Open IIS Manager, locate the site, click on the folder you want to protect, then click on IP address and Domain Restrinctions.
Also click on "Edit feature settings" in the right Actions panel" to specify actions for unspecified clients (i.e. Deny with Forbidden, or simply Deny With Not Found).
This will generate the right configuration for you.
In your root web.config use the location element:-
<location path="Secure">
<system.webServer>
<security>
<ipSecurity allowUnlisted="false">
<clear/>
<add ipAddress="192.168.100.1" />
<add ipAddress="169.254.0.0" subnetMask="255.255.0.0" />
</ipSecurity>
</security>
</system.webServer>
</location>

Why is a module removed in the root folder's web.config being used in a subfolder?

My application has this structure
MyApplication
-Themes
In my application's webconfig I remove the UrlAuthorization module and add my own:
<modules runAllManagedModulesForAllRequests="true">
<remove name="UrlAuthorization" />
<add name="MyModule" type="MyType, MyNamespace" preCondition="managedHandler" />
</modules>
My Theme folder has this webconfig (this is the complete webconfig):
<?xml version="1.0"?>
<configuration>
<system.web>
<pages styleSheetTheme="" validateRequest="false" />
</system.web>
</configuration>
I have this deployed in 3 environments. 2 of them works correctly but in one of them I have the UrlAuthorization module working when I make a request do a file inside the Theme folder.
I know that the UrlAuthorization is active because I do not get the resource I requested, but an URL /ReturnURl/... path
The < remove> tag is working because removing it causes the whole request to be redirect to the /ReturnUrl
Is there any reason that may cause this behavior to happen only in this machine?
I deployed all of them and I do not remember making and different task on any of them
thanks!
FYI, it was an issue due to the folders permissions in the file system. I made the environments identical and it worked.

Multiple Web.Config files - from a code point-of-view

ASP.NET allows for Web.Config files at sub-levels within a site structure. However, I can't find any articles discussing how this looks from code.
In the Orchard CMS there are config files all over the shop. There's even a config file in a folder containing only .CSS files! I'm new to larger-scale ASP.NET apps so...
Can someone just tell me if I'm right in my assumptions.
The config file can affect server settings, and hence how a server processes a request to a resource further down a site structure.
From a code point of view, if the same line of code in the same class queries the config file during a request for http://level1.resource then it could read a different value to when the same code executes during a request for http://level1/level2.resource (if there is a web.config at level2)
Overall, the way it works is based on the current request path.
Right?
you can use the multiple web.config files in the sub-folders level. Each folder will contains its own web.config. Multiple Web.config files can't be used at same level.
Below is code for root folder web.config
<?xml version="1.0"?>
<configuration>
<system.web>
<compilation debug="true" targetFramework="4.0" />
</system.web>
<appSettings>
<add key="root" value="This is from root web.config"></add>
<add key="MySetting" value="This my settings is from root web.config"></add>
</appSettings>
</configuration>
and following is code for sub folder web.config.
<?xml version="1.0"?>
<configuration>
<system.web>
</system.web>
<appSettings>
<add key="sub" value="This is from sub web.config settings"></add>
<add key="MySetting" value="This my settings is from sub folder web.config"></add>
</appSettings>
</configuration>
In C# , you can access the settigns of different configuration files as below.
System.Web.Configuration.WebConfigurationManager.AppSettings.Get("Root");
System.Web.Configuration.WebConfigurationManager.AppSettings.Get("MySetting")
This is an old question, but I just asked myself the same one specifically in the Orchard CMS context as per the original post, and I've got an Orchard specific answer.
In the root of Orchard CMS, there's a web.config that want to prevent all users from requesting individual static files. For example, you don't want people to download placement.info or theme.txt from the Theme folders. This is a good "block everything, allow what you need" approach.
<handlers accessPolicy="Script">
<!-- Clear all handlers, prevents executing code file extensions or returning any file contents. -->
<clear />
<!-- Return 404 for all requests via a managed handler. The URL routing handler will substitute the MVC request handler when routes match. -->
<add name="NotFound" path="*" verb="*" type="System.Web.HttpNotFoundHandler" preCondition="integratedMode" requireAccess="Script" />
[...]
</handlers>
The thing is, in subfolders such as Scripts (containing static js files), Styles (containing only lots of static css files), or Content (contains static images for example), you of course want to allow the web browser to request an individual file. So in these subfolders, you have an extra web.config that looks like this:
<handlers accessPolicy="Script,Read">
<!-- For any request to a file exists on disk, return it via native http module. AccessPolicy="Script" above is to allow for a managed 404 page. -->
<add name="StaticFile" path="*" verb="*" modules="StaticFileModule" preCondition="integratedMode" resourceType="File" requireAccess="Read" />
</handlers>
PS: I'm currently playing with Themes, and for some reason I've had to add <remove name="StaticFile"/> before each <add name="StaticFile"...> in all these child web.config.

Resources