asp.net-mvc MVC 捆绑客户端缓存

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/14374656/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-08 02:32:59  来源:igfitidea点击:

MVC bundle client caching

asp.net-mvcasp.net-mvc-4resourcebundle

提问by stian.net

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)?

默认情况下,MVC 包在客户端上缓存 1 年。是否可以手动设置它的客户端标头(对于 1 个特定的包)?

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.

我需要的是为我的捆绑包之一设置自定义过期标头。我不能依赖“v=hash”查询字符串,因为这个包是针对外部网站的,每次我更改它时,他们都不会更改指向我的包的 url。

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.

我尝试过的是创建一个自定义 Bundle 类(继承 Bundle)并覆盖 GenerateBundleResponse() 方法。通过这种方式我可以控制服务器缓存,但是自定义客户端缓存的唯一方法是设置 BundleResponse.Cacheability(公共、私有、nocache 等)。但我无法手动设置标题。我可以访问 BundleContext(它是 HttpContext),但是当我在该上下文上设置标头时,它也会对所有其他请求产生影响。

采纳答案by Kambiz Shahim

Unfortunately there is no way. You can find the reason in the internal implementation of bundling. in the BundleHandlerclass ProcessRequest calls the ProcessRequest, internal method of the Bundleclass and it calls SetHeaders just before the HttpContext.Response.Write. Therefore the client cache is set to one year just before the response write.

不幸的是没有办法。您可以在捆绑的内部实现中找到原因。在BundleHandler类 ProcessRequest 中调用该类的ProcessRequest内部方法,Bundle并在HttpContext.Response.Write. 因此,客户端缓存设置为响应写入前一年。

Note: BundleHandleris a internal sealed class: internal sealed class BundleHandler : IHttpHandler

注意:BundleHandler是内部密封类: internal sealed class BundleHandler : IHttpHandler

In the BundleHandlerclass:

BundleHandler课堂上:

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 Bundleclass:

Bundle课堂上:

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

回答by Adam

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:

ASP.NET MVC 捆绑功能的默认行为是,如果构成包的任何文件发生更改 - 该包的查询字符串将自动更改 - 假设您在视图代码中使用以下内容:

@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

每次我更改它时,他们都不会更改指向我的包的 url

回答by Jarrod Moura

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.

似乎对我有用的是在包配置中给包一个版本号,然后在你的标记中引用新版本。

回答by Adilson de Almeida Jr

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.

虽然没有更好的方法来设置包可缓存性,但您可以创建一个 HttpModule 来识别对包的请求并设置内容可缓存性。

You have the same effect doing this on the Global.asax:

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

回答by Saikat Ghosh

Pass an extra query string parameter to the url and change it everey time you want the cache to refresh.

将额外的查询字符串参数传递给 url,并在每次希望刷新缓存时更改它。

e.g: https://www.google.co.in/?gfe_rd=cr&ei=EwJeVbHWLcX08wfgwoCoBA&gws_rd=ssl&custom=abc

例如:https: //www.google.co.in/?gfe_rd=cr&ei=EwJeVbHWLcX08wfgwoCoBA&gws_rd=ssl&custom=abc

the last parameter is custom.

最后一个参数是自定义的。

回答by Brandon

This is a modification of Adilson's answer, but without having to create an HttpModule:

这是对 Adilson 答案的修改,但无需创建 HttpModule:

In the global.asax.cs of the MVC project:

在MVC项目的global.asax.cs中:

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