Output caching in ASP.NET with text substitution and QueryString criteria

To speed up a dynamic website that I maintain, I decided to use the output caching mechanism built into ASP.NET. I wanted to bypass the page lifecycle and database roundtrips to achieve a faster loading webpage. However I was faced with a couple problems which are worth noting.

Memory is more expensive than disk space so it was important that I cache the webpages to disk rather than in-memory. I therefore hook up a custom output cache provider by leveraging the features of ASP.NET 4.0 and tailored it for disk caching. This worked fine for the caching portion but I also needed to evict certain pages based on user actions.

Deleting output cache for a single page based on QueryString

Delete the cache version of a page like “Product.aspx” is easy but if you wanted to evict “Product.aspx?id=4” from the cache, then there’s no easy solution. There is only one method to delete cache : HttpResponse.RemoveOutputCacheItem which takes a virtual absolute path to work. When I looked at the code through reflection, I saw that this method was taking in parameters like the path to the webpage, the HttpVerb (GET or POST), the VaryBy parameters to generate a cache key for that particular webpage. To evict an item from the cache programmatically, you would need to know the cache key and it’s actually difficult to recompute the key without making your own method which will be an overkill.

To overcome the problem, I changed the custom cache provider and had a static RemoveItemFromCache method which takes in a cached key and deletes the file. I analysed the cached key that was being generated (eg a2/product.aspxHQNidV4FCDE) and realised that after HQN it was the QueryString parameter that was inserted and before FCDE it was the value of the QueryString. I could therefore delete a particular page by entering the cache key. Note that when the cached files are saved to disk, the “/” in the file path are replaced with an underscore “_”.

Session is not accessible when output caching is enabled

Full page caching is better than caching multiple parts of the webpage through user controls as you will see a better performance with the former. However I needed to have login links for anonymous users (users which are not logged in yet) and logout links for those who are currently signed in. There’s a substitution control which you can use for that matter :

<asp:Substitution ID=”MySubstitutionControl” MethodName=”” runat=”server” />

The MethodName that you specify will return a string which will be inserted in the cached page. This is known as donut caching as you’re caching the whole page but have holes in it (placeholders) where you can insert dynamic text.  You can also use Response.WriteSubstitution in your code behind if you don’t like the idea of declarative syntax.

The problem with the substitution control however is that the Session object is not accessible. I was storing the username of the currently logged in user in the session but with the output caching, I was not able to retrieve this info from the session as it was always null. It makes sense though because when using output caching you’re bypassing the asp.net page life cycle and the session object is not populated and the page is rather retrieved from cache. For me, it was important to display the username with a logout link though and I had to find a solution to it.

My workaround was to use a cookie to store the username when people sign in. Since the cookies collection is available (as well as the request object), I was able to display the logout link with the username properly.

AdRotator does not do post cache substitution when the AdCreated method has been overridden

The ASP.NET team says that the AdRotator control has built in post cache substitution and while this is true, you will encounter problems if you override the AdCreated Event. By doing this, you’re losing the post cache features and you’ll notice that an advertisement banner that was shown on the first view of a page before it is cached will be shown on subsequent views of that page because substitution will not occur. To fix the problem, you’ll have to come with your own post caching mechanism for this control or build your own custom control to display dynamic banners which is not a tough task.

Server.Transfer will not cache out page response

On one particular page, I was using a Default.aspx page in the root to transfer to another page where the logic is run. This is because I wanted a particular folder to display the same content (varied by querystring) from a different page. The server.transfer method worked fine except when I enabled caching on the transferred to page. So if Page A is transferred to Page B and Page B has the output cache, you’ll find that Page A won’t be output cached.

The solution to this problem is to use Server.Execute instead of Server.Transfer and Page A will be cached.

Server.Execute(“~/MyPageA.aspx”, Response.Output);

Programmatically using output cache in code behind

I found that using Response.Cache with multiple VaryByParam parameters does not work well. So if you use this:


Response.Cache.SetExpires(DateTime.Now.AddSeconds(36000));
Response.Cache.SetCacheability(HttpCacheability.Server);
Response.Cache.VaryByParams["p"] = true;
Response.Cache.VaryByParams["s"] = true;
Response.Cache.VaryByParams["t"] = true;
Response.Cache.SetVaryByCustom("RawUrl");
Response.Cache.SetValidUntilExpires(true);

You’ll have to use this instead :

Response.Cache.SetExpires(DateTime.Now.AddSeconds(36000));
Response.Cache.SetCacheability(HttpCacheability.Server);
Response.Cache.VaryByParams["*"] = true;
Response.Cache.SetVaryByCustom("RawUrl");
Response.Cache.SetValidUntilExpires(true);

The wildcard (*) will cache your pages properly.

Google Chrome malware warning

The day before yesterday, I went to check my emails on Hotmail and to my surprise I saw a big malware warning from Google Chrome telling me that a particular domain is known to distribute malware and I should really not proceed with my request. I was shocked because Hotmail is a big company and a trusted site and I don’t think it would be possible to hack into the website to make it distribute malware. I googled the problem and could not find anything on the topic. I went onto Twitter as well just to make sure that it was not something that Google has not picked up yet but social networking site Twitter has because it’s more real time. Unfortunately there was not a single clue.

However the domain that was flagged as malware seemed familar but I could not remember what exactly though. After some time digging into my rusty brain, I finally realised that this domain (completely unassociated with Hotmail) was the status message of someone I know on MSN Messenger. It was someone who was into Graphic Design that I added to my contacts. It then occurred to me that his site was compromised and because his website link is in my contacts list on Hotmail, Google Chrome was warning me of the potential danger of clicking through this link. It was not very obvious though and it appeared that Hotmail was the one which was hacked into.

After removing this person from my contact list in Hotmail, the malware message no longer appeared when I checked my emails. So if you see this kind of message when browsing a trusted site, then it could be because one of the websites links on that website has been flagged as malware.

Configuring Windows Firewall to use IP range to restrict access to your server

To keep your Windows 2008 server secure, it is important to restrict access to who is allowed to connect to your server. You may have programs like FileZilla, SQL Server/MySql and Remote Desktop and just having a strong password for those services is not enough. You should therefore create Windows Firewall inbound rules to prevent unauthorised access to your server.

My previous ISP gave me an IP address which lasted at least 6 months before being renewed. So I could add my IP address to the Windows Firewall and deny access to all others. That worked fine until I changed ISP and everytime I connect I was given a new IP address. This caused a huge problem for me and I needed to find a solution. I didn’t want to remove that Windows Firewall rule and just have the password as a method of defence against hackers or opportunists.

So I started analysing the IP addresses that were assigned to me by my new ISP every day or so. From the data I gathered, I found a pattern which shows that the first 2 digits were fixed, the 3rd one varied between just two consecutive numbers and the fourth one was dynamic (as in it could be any number). With that information, I knew exactly what I had to do. Instead of listing a static IP in Windows Firewall, I could set an IP range which could connect to the server.

Let’s suppose, the lowest IP address was 140.192.45.0 and the highest was 140.192.46.255. You could put that in the Firewall inbound rule easily as the starting from and upto range as follows:

IP address range

However there’s a more elegant way of doing that – by specifying a subnet mask. You could try type in 140.192.45.0/23 as follows:

ip address subnet mask

I’ve used the subnet calculator to correctly assign the range : http://www.subnet-calculator.com/cidr.php

Note that although this configuration will allow a lot of IP addresses within that range to access the server, it’s better than leaving it open to the whole world. The combination of the IP range along with a strong password makes it more secure than having no IP restriction at all.