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.
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;
}
}
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
对我来说似乎有效的方法是在捆绑软件配置中为捆绑软件提供一个版本号,然后在您的标记中引用新版本。
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);
}
}
Pass an extra query string parameter to the url and change it everey time you want the cache to refresh.
eg: https://www.google.co.in/?gfe_rd=cr&ei=EwJeVbHWLcX08wfgwoCoBA&gws_rd=ssl&custom=abc
the last parameter is custom.
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));
}
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.