Reputation: 3963
By default a MVC bundle is cached on client for 1 year. Is it possible to set it's client headers manually (for 1 specific bundle)?
What I need is to set custom expire headers for one of my bundles. I can't rely on the "v=hash" querystring because this bundle is for an external website, and they won't change the url pointing to my bundle each time I change it.
What I've tried is to create a custom Bundle class (inherit Bundle) and overridde the GenerateBundleResponse() method. This way I can control the server caching, but the only way of customizing client caching is to set BundleResponse.Cacheability (public, private, nocache etc). But I can't set headers manually. I have access to the BundleContext (and it's HttpContext), but when I set headers on that context, it will have effect for all other requests as well.
Upvotes: 30
Views: 24761
Reputation: 745
This is a modification of Adilson's answer, but without having to create an HttpModule:
In the global.asax.cs of the MVC project:
protected void Application_EndRequest(object sender, EventArgs e) {
if (Request.RawUrl.Contains("/bundles/")) {
// My bundles all have a /bundles/ prefix in the URL
Response.Cache.SetExpires(DateTime.Now.AddHours(2));
}
}
Upvotes: 1
Reputation: 31
What seems to work for me is giving the bundle a version number in the bundle config, then reference the new version in your markup.
Upvotes: 3
Reputation: 2590
Unfortunately there is no way. You can find the reason in the internal implementation of bundling. in the BundleHandler
class ProcessRequest calls the ProcessRequest
, internal method of the Bundle
class and it calls SetHeaders just before the HttpContext.Response.Write
. Therefore the client cache is set to one year just before the response write.
Note: BundleHandler
is a internal sealed class: internal sealed class BundleHandler : IHttpHandler
In the BundleHandler
class:
public void ProcessRequest(HttpContext context)
{
if (context == null)
{
throw new ArgumentNullException("context");
}
context.Response.Clear();
BundleContext context2 = new BundleContext(new HttpContextWrapper(context), BundleTable.Bundles, this.BundleVirtualPath);
if (!Bundle.GetInstrumentationMode(context2.HttpContext) && !string.IsNullOrEmpty(context.Request.Headers["If-Modified-Since"]))
{
context.Response.StatusCode = 304;
}
else
{
this.RequestBundle.ProcessRequest(context2);
}
}
In the Bundle
class:
internal void ProcessRequest(BundleContext context)
{
context.EnableInstrumentation = GetInstrumentationMode(context.HttpContext);
BundleResponse bundleResponse = this.GetBundleResponse(context);
SetHeaders(bundleResponse, context);
context.HttpContext.Response.Write(bundleResponse.Content);
}
private static void SetHeaders(BundleResponse bundle, BundleContext context)
{
if (bundle.ContentType != null)
{
context.HttpContext.Response.ContentType = bundle.ContentType;
}
if (!context.EnableInstrumentation)
{
HttpCachePolicyBase cache = context.HttpContext.Response.Cache;
cache.SetCacheability(bundle.Cacheability);
cache.SetOmitVaryStar(true);
cache.SetExpires(DateTime.Now.AddYears(1));
cache.SetValidUntilExpires(true);
cache.SetLastModified(DateTime.Now);
cache.VaryByHeaders["User-Agent"] = true;
}
}
Upvotes: 12
Reputation: 1
Pass an extra query string parameter to the url and change it everey time you want the cache to refresh.
e.g: https://www.google.co.in/?gfe_rd=cr&ei=EwJeVbHWLcX08wfgwoCoBA&gws_rd=ssl&custom=abc
the last parameter is custom.
Upvotes: 0
Reputation: 2755
While there is not a better way to setup bundles cacheability, you can create a HttpModule which identifies the requests to the bundle and set the content cacheability.
You have the same effect doing this on the Global.asax:
public override void Init()
{
this.EndRequest += MvcApplication_EndRequest;
base.Init();
}
void MvcApplication_EndRequest(object sender, EventArgs e)
{
var request = this.Request;
var response = this.Response;
if (request.RawUrl.Contains("Content/"))
{
response.Cache.SetCacheability(HttpCacheability.NoCache);
}
}
Upvotes: 2
Reputation: 28858
The default behavior of the ASP.NET MVC bundling feature is that if any of the files that compose a bundle change - the query string for that bundle will automatically change - assuming you are using the following in your view's code:
@Scripts.Render("bundle name")
So this means if you have a new version of a file that is in a bundle, the next time your page renders a view that uses that bundle, it will send a script tag that the client browser will not found in its cache (since the query string is different).
So it seems like this will solve your problem - depends on what you mean by:
and they won't change the url pointing to my bundle each time I change it
Upvotes: 4