I'm trying to enable gzip compression on a site on our work intranet. Unfortunately, I don't have access to IIS, so any changes I make have been through web.config.
The server is running IIS 6 and .NET 2.0.
I enabled compression by adding an httpmodule
public class EnableCompression : IHttpModule
{
public void Init(HttpApplication application)
{
application.BeginRequest +=
(new EventHandler(this.Application_BeginRequest));
}
private void Application_BeginRequest(Object source, EventArgs e)
{
HttpContext context = HttpContext.Current;
context.Response.Filter = new GZipStream(context.Response.Filter, CompressionMode.Compress);
HttpContext.Current.Response.AppendHeader("Content-encoding", "gzip");
HttpContext.Current.Response.Cache.VaryByHeaders["Accept-encoding"] = true;
}
}
I registered in the web.config...
<system.web>
<httpModules>
<add name="EnableCompression" type="EnableCompression"/>
</httpModules>
</system.web>
Well, the above worked fine, except javascript and css files do not get compressed. From what I have found, I would have to add .js and .css to the application mappings in IIS 6, but of course I can't do that.
Apparently this can be done via the web.config file, but I don't know how to do that.
How can I enable compression for .js and .css files?
In IIS6, static code is not processed by managed HttpModules; it requires a native ISAPI.
One trick you can use is to convert your *.js and *.css files into dynamic files. You do this by changing them to be *.aspx, and set the ContentType to the right MIME type. For example:
this.Response.ContentType = "application/x-javascript";
The only other trick is to set StyleSheetTheme="" in the Page directive in the markup file. Otherwise, the runtime will insist on a <head> section in the document. You can enable output caching to minimize the performance impact.
I wrote a blog post about this on the JS side, in case it helps (CSS is similar, just with a different MIME type): http://www.12titans.net/p/dynamic-javascript.aspx
Unfortunately, this requires changing the name of your JS and CSS files in your app, but if you want compression and don't have access to IIS, I don't think there's a way around that.
If you want to keep the *.js and *.css extensions, you can do so by adding a handler for them in your web.config. For example:
<compilation>
<buildProviders>
<add extension=".css" type="System.Web.Compilation.PageBuildProvider"/>
</buildProviders>
</compilation>
<httpHandlers>
<add path="*.css" verb="*" type="System.Web.UI.PageHandlerFactory"
validate="true"/>
</httpHandlers>
This helps from a naming perspective, but not performance; the files will still be dynamic -- they are basically *.aspx files, but with a different extension. It also doesn't work correctly with ASP.NET Themes, since pages in the Themes folder can't be dynamic, regardless of their extension.
Adding to #RickNZ's answer:
Be careful about converting to dynamic. IIS is much faster at serving static files than dynamic (kernel mode). Only consider that if your app is dominated by bandwidth concerns.
One other option is to look into CDNs for static content (content delivery networks). Azure, Amazon, Akamai and others offer services. That's a very fast geo-located friendly way of serving static files.
Related
I have the need to secure an entire folder of static HTML files. The intention is that a user cannot access these files unless they are authenticated and have the necessary role.
We've got cookie-based authentication set up using OWIN, but no matter what I try I can't seem to figure out the correct combination of changes to make to require authentication on the folder.
The first problem is that IIS is skipping ASP.NET completely and just serving the files. I think there's probably a way around that by setting runAllManagedModulesForAllRequests to true. But where do I go from there?
I've tried stuffing elements in the Web.config to require the proper roles, but it just results in EVERY request getting denied (presumably because it's not inspecting the proper cookie or something).
I've spent my entire day on this and I'm about to lose my mind.
Has anyone solved this problem?
IIS is serving static files , if you want to stop this you can remove default static file handler and than every request is
serverd by MVC/OWIN.
Than make static file handling and authorization in your controller
: listen/map route where static files are located
to remove default static file handler add this to web.config file:
<configuration>
<system.webServer>
<handlers>
<remove name="StaticFile" />
</handlers>
</system.webServer>
</configuration>
I need advice or suggestions on how to add Expires Headers to my CSS, Image and JavaScript files in ASP.Net MVC.
A key issue is that the software is not in a single location. It is distributed to clients who handle the hosting so I would rather have a solution that doesn't require manual configration in IIS unless it's unavoidable!
I googled around and the majority of answers seem to be focused on content that is returned via a controller. Can't do that for JavaScript files though..
Which IIS Version are you using?
If by 'manual IIS configuration' you mean having to open the IIS manager console, IIS 7.5 (and I think 7 as well) allows you to add expires headers to static content using only the web.config:
<system.webServer>
<staticContent>
<clientCache cacheControlMode="UseMaxAge" cacheControlMaxAge="00:30:00" />
</staticContent>
</system.webServer>
You can do something like this by writing a custom handler for your javascript files. In your Web.Config file of your MVC project look for the httpHandlers section. Add something like the following line:
<add verb="GET" path="/YourScriptsFolder/*.js" type="Your.Project.Namespace.And.Custom.Handler, Your.Assembly.Name" validate="false" />
This will force all requests for js files in that folder through your custom handler which will look something like this:
class CustomHandler : IHttpHandler
{
#region IHttpHandler Members
public bool IsReusable
{
get { return true; }
}
public void ProcessRequest(HttpContext context)
{
// Set any headers you like here.
context.Response.Expires = 0;
context.Response.Cache.SetExpires(DateTime.Parse("3:00:00PM"));
context.Response.CacheControl="no-cache";
// Determine the script file being requested.
string path = context.Request.ServerVariables["PATH_INFO"];
// Prevent the user from requesting other types of files through this handler.
if(System.Text.RegularExpressions.RegEx.Match(path, #"/YourScriptsFolder/[^/\\\.]*\.js"))
context.Response.Write(System.IO.File.ReadAllText(path));
}
#endregion
}
I haven't tested this code so you might run into some issues but this is the basic idea. There are a plethora of examples on ASP.Net custom handlers throughout the web. Here's a good example:
http://www.developer.com/net/asp/article.php/3565541/Use-Custom-HTTP-Handlers-in-Your-ASPNET-Applications.htm
Another, less complicated option is to add a randomized or versioned query string to the end of the file path.
<link rel="stylesheet" type="text/css" href="somecssfile.css?version=1.0.0.3" />
When you change the version, the browser will get a new copy of the file.
I added the following lines to Application_Start method in global.asax:
var provider = new TestVirtualPathProvider();
HostingEnvironment.RegisterVirtualPathProvider(provider);
Yet the 'TestVirtualPathProvider' is never used when deploying this application in IIS6 (it does in the ASP.NET Development Server).
Edit: the default path provider has always done its job correctly and served (non-embedded) views correctly. The problem is simply that I want to use my own path provider to provide embedded views. So, initially, I already had the following wildcard mapping configured:
Any possible reasons why this does not work in IIS6?
Are there any other factors (handlers for example) wich might influence the used VirtualPathProvider?
UPDATE: the fact that you want to handle extension-less URL's is an important point that's not mentioned in the question. Please see this page for help in setting up MVC with IIS 6: http://haacked.com/archive/2008/11/26/asp.net-mvc-on-iis-6-walkthrough.aspx. This should cover your scenario as well.
Most likely the same issue that I answered in this thread: http://forums.asp.net/t/995633.aspx
Basically, add this in your web.config:
<httpHandlers>
<add path="*" verb="GET,HEAD,POST" type="System.Web.StaticFileHandler" validate="true" />
</httpHandlers>
That other thread has some details that explain why this is necessary.
For the combination Custom VPP + IIS6 + Precompiled site, we need to add the VPP from AppInitailize();
public static class AppStart
{
public static void AppInitialize()
{
// code to be executed automatically by the framework
}
}
See also:
http://sunali.com/2008/01/09/virtualpathprovider-in-precompiled-web-sites/
I believe that you need to use an ISAPI filter in IIS6 to intercept URLs without extensions. Problem is that ISAPI will need to be done in c/c++.
IIS6 is configured to allow only certain extensions to be processed by the ASP.net pipeline.
To findout how you can redirct requests check out the post by DocV.
In an ASP.NET application, I need to do some changes on every CSS file sent.
So I created an HttpHandler (inside the app itself), added:
<add verb="*" path="*.css" type="MyWebsite.CssTestHandler,MyWebsite"/>
to Web.config in system.web/httpHandlers and modified the handler like this:
public void ProcessRequest(HttpContext context)
{
context.Response.Clear();
context.Response.Write("Hello World");
context.Response.End();
}
But CSS files are still just like they were before, so the handler is just ignored.
What I'm doing wrong?
You need to setup a wildcard map in IIS, see the following link:
http://www.microsoft.com/technet/prodtechnol/WindowsServer2003/Library/IIS/5c5ae5e0-f4f9-44b0-a743-f4c3a5ff68ec.mspx?mfr=true
This will cause the request for the CSS file to be served by ASP.NET rather than just IIS.
If the application serves very high traffic, consider setting this mapping for .css files only, or even better change the CSS data in the page rather than changing the file.
Check this page for instructions on all 3 cases of IIS version (6, 7 Classic pipeline and 7 Integrated pipeline):
http://learn.iis.net/page.aspx/508/wildcard-script-mapping-and-iis-7-integrated-pipeline/
According to it, in case of Integrated pipeline, you need to add the following config parameter:
runAllManagedModulesForAllRequests="True"
The App ignores your CSS files because IIS ignores CSS files.
It's not mapped to an executable in IIS.
alt text http://www.fastpics.net/sharepics/imih41904722.jpg
Try adding the .css extension and map it to the .NET dll.
Let me state off the bat that I'm not that familiar with ASP.Net MVC, and I don't think I have time for this project to become familiar and switch.
I'm building a website where there's only one physical page, mysite.com/default.aspx. If a user browses to mysite.com/foo/bar, I (somehow) want my default.aspx to handle that request. I know it sounds very "route"/"controller" oriented...but is there some way to do this without switching over whole-hog to MVC?
Also of note is that the site will also have static images and things that I don't want served up by my page...so the resulting html of mysite.com/foo/bar may have html that includes an img tag with a src of mysite.com/images/foo.gif, so I need to be able to preclude certain folders/files/whatnot from being processed.
What you're talking about is called URL rewriting and yes, an ASP.Net forms application is capable of it. This entry seems to explain the technique fairly well here:
http://weblogs.asp.net/scottgu/archive/2007/02/26/tip-trick-url-rewriting-with-asp-net.aspx
To allow HTTP rewrites to work on files that do not exist in IIS 6 you'll need to implement wildcard mapping. ASP.Net MVC falls prey to this same issue so even though you're not using ASP.Net MVC this article is still relevant:
http://haacked.com/archive/2008/11/26/asp.net-mvc-on-iis-6-walkthrough.aspx
Scroll down to the heading: "IIS6 Extension-less URLs"
If you need to figure out how to get to the IIS property window displayed scroll up a bit for some context. Just ignore the part about .mvc extensions.
You could probably set up IIS to redirect all 404 requests to your website. If I remember correctly, there's a bug in IIS 6 (if that's what you're running) that will return a 200 status code, allowing you to do your normal processing.
Otherwise, you could use the tag in your web.config to handle the redirect--however, I think this might return the 404 error code.
Hope that helps!
I think I have something working, in IIS7...but it seems kind of brittle:
I have an HttpModule, setup like this:
<modules runAllManagedModulesForAllRequests="true">
<remove name="ScriptModule" />
<add name="ScriptModule" preCondition="managedHandler" type="System.Web.Handlers.ScriptModule, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
<add name="MyRewriteModule" preCondition="managedHandler" type="MyWeb.MyRewriteModule, MyWeb" />
</modules>
and my module has code as follows:
void context_BeginRequest(object sender, EventArgs e)
{
HttpApplication app = (HttpApplication)sender;
HttpRequest request = app.Context.Request;
List<string> ignoreExtensions = new List<string>() { "axd", "gif", "ico" };
if(ignoreExtensions.TrueForAll(s => !request.FilePath.ToLower().EndsWith(s)))
app.Context.RewritePath("~/default.aspx", request.Path, request.QueryString.ToString(), true);
}
Obviously I would cache and expand that list...but overall it seems to work. Can anyone point out any obvious drawbacks?
Just modify your 404 error pages to redirect to default.aspx.