Output (server-side) caching of ashx files - c#

I am trying to enable output caching on all ashx files in my site. I'm trying to keep the server from generating the file on each request - NOT trying to tell the browser to cache the file.
I've distilled my ashx file down to this simple code:
public class DefaultStyles : IHttpHandler
{
public void ProcessRequest(HttpContext context)
{
context.Response.ContentType = "text/css";
StringBuilder styleSheet = new StringBuilder();
styleSheet.AppendLine(string.Format("/* Generated: {0}, {1} */", DateTime.Now.ToShortDateString(), DateTime.Now.ToLongTimeString()));
context.Response.Write(styleSheet.ToString());
}
}
And in my web.config file, i have this:
<system.webserver>
<caching enabled="true">
<profiles>
<add extension=".ashx" policy="CacheForTimePeriod" duration="00:01:00" />
</profiles>
</caching>
</system.webserver>
Still, every request i make to the ashx file generates a new version with the current date and time in it.
What am i doing wrong?
Thanks.

Maybe this will help?
http://objectmix.com/dotnet/393333-output-caching-custom-http-handler.html
It would seem to suggest that since you're not using the normal page handler, the output caching module isn't invoked.
Simon

I was actually doing everything right, just not testing in the right environment. Once i deployed the simple example above on our IIS 7 server, it worked as expected with the code above. It should be noted that this DID NOT work on our IIS 6 server. I had not realized that this was a new feature only available in IIS 7

Related

Pages don't display correctly after adding module in system.webserver

I'm trying to implement a custom http security module that uses the roles in the sitemap to control access to pages (instead of having to store it all in the web.config as well). Following article here: http://www.codeproject.com/Articles/8728/Extending-ASP-NET-security
I've updated it for the newer versions of IIS, adding the module in system.webServer instead
<system.webServer>
<modules>
<add name="SecurityHttpModule" type="DINO.SecurityHttpModule"/>
</modules>
</system.webServer>
Everything seems to be working alright in respect to that, but pages are no longer rendering correctly. If I look at the console in Chrome I am seeing errors like
Resource interpreted as Stylesheet (or Script) but transferred with MIME type test/html: "http://localhost:57855/login"
and
Uncaught SyntaxError: Unexpected token < (about the <!DOCTYPE html> at the top of the page)
I assume I'm just missing something else I need to do when I'm adding a custom module, but I have not yet been able to find any reference to this issue.
Oguz Ozgul was correct in his comment. To fix it, I added a list of extensions I want to validate permissions for and then I check that as the first part of my authenticate request method.
private static readonly List<string> extensionsToValidate = new List<string>(new string[] { ".aspx", "" });
private void AuthenticateRequest(Object sender, EventArgs e)
{
//Ignore specified extensions from redirection
string CurrentExt = Path.GetExtension(HttpContext.Current.Request.Url.LocalPath);
if (extensionsToValidate.Contains(CurrentExt))
{
//do all security check work here
}
else return;
}

How to add a cache buster to all images in an ASP.NET MVC site

I am trying to improve the performance of a large ASP.NET MVC website and I am currently looking for a way of adding a cache busting query string to image requests in such a way that I don't have to go through all the views and CSS and change each image reference.
Desired result
To verify if the cache buster is being added I am using the Firebug extension in Firefox and what I am looking to see is something like this (screenshot taken from another website)
What I've tried
The simplest answer seemed to me to create a custom HttpModule that intercepted any image request and then appended the cache busting query string to the request URL. So I wrote the following class
public class CacheBusterImageHandler : IHttpModule
{
public void Init(HttpApplication context)
{
context.BeginRequest += this.BeginRequest;
}
public void Dispose()
{
}
public void BeginRequest(object sender, EventArgs e)
{
// This conditional check will need to be modified to capture other image types
// but for testing purposes it is good enough
if (HttpContext.Current.Request.Path.EndsWith(".gif", StringComparison.OrdinalIgnoreCase))
{
// The version number will be generated but again for testing purposes this
// is good enough
var pathWithCacheBuster = string.Format("{0}?v1.0.0.0", HttpContext.Current.Request.Path);
HttpContext.Current.RewritePath(pathWithCacheBuster);
}
}
}
I then registered the module in web.config like this
<system.webServer>
<modules runAllManagedModulesForAllRequests="true">
<add name="CacheBusterImageHandler" type="MyAssembly.CacheBusterImageHandler" preCondition="managedHandler" />
...
I then verified that the requests were getting processed by the module by using breakpoints, however when I checked in Firebug the image requests did not have the cache buster appended to URL. I then decided to read the documentation for the RewritePath method and found that of course it simply redirects the request but does not alter the requested URL.
Questions
Is there a way in an HttpModule to append the cache buster to the query string?
If not, is there some other way I can achieve the same result without having to modify every reference to an image?
"Is there a way in an HttpModule to append the cache buster to the
query string?"
No. That is far to late in the process. The URL has to be changed when you put it in the page, that's what the browser uses to check if the image is in the cache or not.
"If not, is there some other way I can achieve the same result without
having to modify every reference to an image?"
That depends on how you put the image URLs in the page, but there is no way of changingt he URLs that works for all way to put an URL in the page.
You can make a method that calculates the version number/string to include, and add that to all URLs. That way you only have to make the change once, not every time an image changes.
The method could use the version number or compile time of the assembly if you want to invalidate the cache every time that you deploy the page, or the update time of the image file.
Basically:
<img src="/images/logo.png<%= ImageVersion("/images/logo.png") %>" alt="Logo">
Using something like:
public static string ImageVersion(string name) {
FileInfo info = new FileInfo(HttpContect.Current.MapPath(name));
int time = (int)((info.LastWriteTimeUtc - new DateTime(2000,1,1)).TotalMinutes);
return "?v=" + time.ToString();
}

How to set up embedded resources in an MVC application

I am trying to serve some JS and CSS files that are embedded into a DLL, with a solution based on this approach here: http://weblogs.asp.net/imranbaloch/asp-net-bundling-and-minification-and-embedded-resources
so, javascript and css files are embedded and I create bundles for them.
My problems start because, having quite a few of them, I need some folder structure to keep order. So the original route
RouteTable.Routes.Insert(0,
new Route("Embedded/{file}.{extension}",
new RouteValueDictionary(new { }),
new RouteValueDictionary(new { extension = "css|js" }),
new EmbeddedResourceRouteHandler()
));
is not enough anymore, so I have changed it to this:
RouteTable.Routes.Insert(0,
new Route("Embedded/{*url}",
new RouteValueDictionary(new { }),
new EmbeddedResourceRouteHandler()
));
I also cannot use the extension part because the catch-all part has to be the last one
So now if I try to access anything that looks like a file, my route will never be used so I will just get a 404
I have tried replacing the dot with a slash or adding a slash at the end but what I'm after here is a simple solution that will allow me to map urls that look like files to actual files.
I've also searched the web and there seem to be solutions based on UrlRewrite or altering the web.config but:
- I would like not to modify the IIS settings for every application to accomodate the library
- since it's a library, I would like it to be self contained and developers that use it shouldn't care about these sort of internal issues
So, is there a solution that I can implement in my library for this?
Also worth mentioning is that the original routing had the same issue, it only worked because of
<modules runAllManagedModulesForAllRequests="true" />
in the web.config, which I don't think is a good idea for performance
When you set
<modules runAllManagedModulesForAllRequests="true" />
this enables all available modules to run against the request. Which, as you mentioned, isn't the best for performance. However, you could add only the module you actually need- in this case the UrlRoutingModule.
You could add this module like this:
<system.webServer>
<modules>
<remove name="UrlRoutingModule-4.0" />
<add name="UrlRoutingModule-4.0" type="System.Web.Routing.UrlRoutingModule" preCondition="" />
</modules>
</system.webServer>
If you want an even better way (IMO) to do this, disregard the WebConfig and add it in a AppStart.cs file in your class library.
using Microsoft.Web.Infrastructure.DynamicModuleHelper;
[assembly: WebActivatorEx.PreApplicationStartMethod(typeof(AppStart), "PreStart")]
[assembly: WebActivatorEx.PostApplicationStartMethod(typeof(AppStart), "Start")]
namespace EmbeddedPages
{
public static class AppStart
{
private static bool PreStartFired = false;
public static void PreStart()
{
if (!PreStartFired)
{
PreStartFired = true;
DynamicModuleUtility.RegisterModule(typeof(UrlRoutingModule));
}
}
}
}
This adds the UrlRoutingModule into the module stack, and your URL's should now properly resolve. Note: you will need to add WebActivator to your project through nuget.

error executing child request for server.transfer with Handler

I have created the handler to process .html pages in my asp.net c# web application.
I also use url rewriting concepts.
Handler works fine when any html requrest come to server/website.
Coding details are as follows:
web.config Handler Code:
<add verb="*" path="*.html," validate="false" type="MyProject.ContentHandler,MyProject" />
ContentHandler.cs Code:
public void ProcessRequest(HttpContext context)
{
string strMapPage = string.Empty;
if (context.Request.Url.ToString().Contains("category"))
{
strMapPage = "/Links.aspx?ID=" + ProducID;
}
else
{
strMapPage = context.Request.Url.ToString();
}
context.Server.Transfer(strMapPage);
}
This method works fine for any .html request like for this page http://localhost:9111/user-category-1.html
But when i try to open the page like '/JS/TinyMCE/imagemanager/index.html'
It throws the error "Error executing child request for /JS/TinyMCE/imagemanager/index.html"
How to solve this problem?
From microsoft:
Microsoft Internet Information Services (IIS) dispatches the Server.Transfer or the Server.Execute request to the appropriate Internet Server Application Programming Interface (ISAPI) extension based on the extension of the requesting file. For example, a request for an .aspx page is dispatched to the Aspnet_isapi.dll ISAPI extension.
After the request is dispatched to appropriate ISAPI extension, the ISAPI extension cannot call another ISAPI extension. You receive the error message "Error executing child request for PageName.asp" because the Aspnet_isapi.dll file, which handles requests to ASP.NET pages, cannot forward the request to the Asp.dll file, which handles requests to ASP pages.
Your haldler is creating problem,
try this:
This is caused by a custom HTTP Handler being added by another application that is most likely being run in the root of your web site. To resolve the problem, modify the web.config file. After add:
<httpHandlers>
<clear />
<add verb="*" path="*.aspx" type="System.Web.UI.PageHandlerFactory" />
</httpHandlers>
I have added the handler in web.config and it resolved the issue.
<add name="tinyMceIndexHandler" verb="*" path="/js/tiny_mce/plugins/imagemanager/pages/im/index.html" type="System.Web.StaticFileHandler" />

MVC with IIS 6

I've read a couple of posts on this issue but still can't seem to get MVC working on IIS 6. I've mapped .mvc to aspnet_isapi.dll in IIS but get a 404 when I browse to my mapped URL which looks like this
RouteTable.Routes.MapRoute("action", "api.mvc/{controller}/{action}", new {action = "Index"});
I then browse to //localhost/Web.Site/api.mvc/Users/List but get a 404 returned
same happens for
//localhost/Web.Site/api.mvc/Users/
I have a UsersController with List and Index that both returns a ViewAction
Is there anything else I need to do? Or have I missed something
cheers
also.............
I should point out that my redirect my default page in the website is working
eg my default code behind has
HttpContext.Current.RewritePath(Request.ApplicationPath, false);
IHttpHandler httpHandler = new MvcHttpHandler();
httpHandler.ProcessRequest(HttpContext.Current);
so the default "/" request does get correctly routed via this in the global.asax.cs
RouteTable.Routes.MapRoute("default", "", new {controller="Home", action = "Index" });
not sure if that helps anyone
Have you unchecked the "Verify that file exists" box on the extension mapping?
Try deleting your default.aspx page and make sure you have this in your web.config:
<httpModules>
<add name="UrlRoutingModule" type="System.Web.Routing.UrlRoutingModule, System.Web.Routing"/>
</httpModules>

Categories

Resources