29

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

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

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

4

6 回答 6

12

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

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

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

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;
    }
}
于 2013-06-05T12:39:07.937 回答
4

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

@Scripts.Render("bundle name")

所以这意味着如果你有一个新版本的文件在一个包中,下次你的页面渲染一个使用这个包的视图时,它会发送一个脚本标签,客户端浏览器在它的缓存中找不到它(因为查询字符串不同)。

因此,这似乎可以解决您的问题-取决于您的意思:

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

于 2013-02-28T02:13:15.270 回答
3

似乎对我有用的是在捆绑配置中为捆绑提供一个版本号,然后在您的标记中引用新版本。

于 2016-07-22T12:35:32.677 回答
2

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

在 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);
        }
    }
于 2014-04-24T19:04:50.720 回答
0

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

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

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

于 2015-05-21T16:07:35.077 回答
0

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

在 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));
    }
}
于 2018-02-13T19:33:07.887 回答