web-dev-qa-db-ja.com

MVCバンドルクライアントキャッシング

デフォルトでは、MVCバンドルは1年間クライアントにキャッシュされます。クライアントヘッダーを手動で設定することは可能ですか(1つの特定のバンドル用)?

必要なのは、バンドルの1つにカスタムの有効期限ヘッダーを設定することです。 "v = hash"クエリ文字列は信頼できません。このバンドルは外部Webサイト用であり、変更するたびにバンドルを指すURLが変更されないためです。

私が試したのは、カスタムBundleクラス(継承Bundle)を作成し、GenerateBundleResponse()メソッドをオーバーライドすることです。この方法でサーバーキャッシングを制御できますが、クライアントキャッシングをカスタマイズする唯一の方法は、BundleResponse.Cacheability(public、private、nocacheなど)を設定することです。しかし、ヘッダーを手動で設定することはできません。 BundleContext(およびHttpContext)にアクセスできますが、そのコンテキストにヘッダーを設定すると、他のすべてのリクエストにも影響があります。

28
stian.net

残念ながら方法はありません。その理由は、バンドルの内部実装にあります。 BundleHandlerクラスでは、ProcessRequestがProcessRequestクラスの内部メソッドBundleを呼び出し、HttpContext.Response.Writeの直前にSetHeadersを呼び出します。したがって、クライアントキャッシュは、応答の書き込みの直前に1年に設定されます。

注: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;
    }
}
11
Kambiz Shahim

ASP.NET MVCバンドル機能のデフォルトの動作は、バンドルを構成するファイルのいずれかが変更された場合-そのバンドルのクエリ文字列が自動的に変更されます-ビューのコードで以下を使用していると仮定します。

@Scripts.Render("bundle name")

つまり、バンドルに含まれるファイルの新しいバージョンがある場合、次にページがそのバンドルを使用するビューをレンダリングするときに、クライアントブラウザがキャッシュにないスクリプトタグを送信します(クエリ文字列が異なります)。

したがって、これはあなたの問題を解決するようです-あなたが何を意味するかによって異なります:

そして、彼らは私がそれを変更するたびに私のバンドルを指すURLを変更しません

4
Adam

私にとってうまくいくように思えるのは、バンドル設定でバンドルにバージョン番号を付け、マークアップで新しいバージョンを参照することです。

3
Jarrod Moura

バンドルのキャッシュ可能性を設定するためのより良い方法はありませんが、バンドルへのリクエストを識別してコンテンツのキャッシュ可能性を設定する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);
        }
    }

これは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));
    }
}
0
Brandon

追加のクエリ文字列パラメーターをURLに渡し、キャッシュを更新するたびに変更します。

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

最後のパラメーターはカスタムです。

0
Saikat Ghosh