3

I have a library of reusable partial views, scripts and images that are embedded in assembly and shared between projects.

Everything works fine, I've modified web.config to make all necessary file types to be served by System.Web.StaticFileHandler, but unfortunately, it serves all resources with Cache-Control: private.

I can write my own StaticFileHandler that would serve VPP content with Cache-Control: public and expiration date.

How do I implement caching support using VirtualPathProvider.GetCacheDependency?

mbergal
  • 635
  • 6
  • 13

2 Answers2

5

I figured out why this happens. I looked at the source for the StaticFileHandler. For embedded files, it doesn't set any of the caching headers. It only does for files in the file system. Meaning this will never work right.

You have two options.

1.Find another http handler. I have never used this but it has come up in my searching: https://code.google.com/p/talifun-web/wiki/StaticFileHandler

2.Create an http module that checks to see if the static file handler was used, if so set the caching details.

Good luck.

Joshua Frank
  • 13,120
  • 11
  • 46
  • 95
Tony
  • 1,684
  • 1
  • 18
  • 29
3

Based on my researches, I found a hacky way to achieve caching.

In your VPP implementation, you should be implementing your own VirtualFile class, extending System.Web.Hosting.VirtualFile base class. It just expects a stream to read file if it is needed from VPP. At that phase, you can inject headers and even change cachability of resource. Because priorly, if I request a static file from VPP, it was coming with a header Cache-Control:private. Actually, server was saying that: I do not care your local caches, etags and so on. I decide whether you should cache it or not. The code below changes it to public and add required e-tag header so that it should stay at cache unless that assembly is changed:

enter image description here

class EmbeddedResourceVirtualFile : VirtualFile
{
    readonly EmbeddedResource embedded;

    public EmbeddedResourceVirtualFile(string virtualPath, EmbeddedResource embedded)
        : base(virtualPath)
    {
        this.embedded = embedded;
    }

    public override Stream Open()
    {
        var assemblyLastModified = embedded.AssemblyLastModified;
        var etag = assemblyLastModified.Ticks;
        var response = HttpContext.Current.Response;
        var cache = response.Cache;
        cache.SetCacheability(HttpCacheability.Public);
        cache.SetETag(etag.ToString());
        cache.SetLastModified(assemblyLastModified);
        cache.SetExpires(assemblyLastModified.AddYears(2));
        return embedded.GetStream();
    }
}

Special thanks for the commentor :)

Community
  • 1
  • 1
Oğuzhan Kahyaoğlu
  • 1,727
  • 2
  • 15
  • 20
  • 2
    Would you mind if I add this to my https://github.com/mcintyre321/EmbeddedResourceVirtualPathProvider/ project under the MIT Licence? – mcintyre321 May 26 '16 at 13:51
  • 1
    It seems, we both wrote approx. same code for same purpose :) Im developing a cms and serving its embedded contents from **multiple assemblies**, which is a private project of my own. I can give you help if you stuck at some point and ofc, you are welcome to use this block. – Oğuzhan Kahyaoğlu May 31 '16 at 07:42
  • Cheers. My project does serve stuff from multiple assemblies also (you have to register them at startup) so you might be able to use it. The main thing it doesn't do is support directories, which is needed for Bundles to work correctly (and is very difficult as the embedded resource names replace both . and / with ., so you can't tell a dir from a . in a filename :( ) – mcintyre321 May 31 '16 at 14:33
  • Oh I see, I dont need bundling at the moment. What is important for me was serving from different assemblies **specifiyng a static path for each**. I will spend some time on this project to see whether it has this feature. – Oğuzhan Kahyaoğlu Jun 01 '16 at 12:04