MVC bundle client caching

落爺英雄遲暮 提交于 2019-11-27 01:51:16

问题


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.


回答1:


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;
    }
}



回答2:


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




回答3:


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.




回答4:


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);
        }
    }



回答5:


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.




回答6:


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));
    }
}


来源:https://stackoverflow.com/questions/14374656/mvc-bundle-client-caching

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!