web-dev-qa-db-fra.com

Mise en cache du client bundle MVC

Par défaut, un ensemble MVC est mis en cache sur le client pendant un an. Est-il possible de définir ses en-têtes client manuellement (pour 1 lot spécifique)?

Ce dont j'ai besoin, c'est de définir des en-têtes d'expiration personnalisés pour l'un de mes ensembles. Je ne peux pas compter sur la chaîne de requête "v = hash", car cette offre est destinée à un site Web externe et ne modifie pas l'URL pointant vers mon offre chaque fois que je le modifie.

Ce que j'ai essayé, c'est de créer une classe Bundle personnalisée (inherit Bundle) et de remplacer la méthode GenerateBundleResponse (). De cette façon, je peux contrôler la mise en cache du serveur, mais la seule façon de personnaliser la mise en cache du client consiste à définir BundleResponse.Cacheability (public, privé, nocache, etc.). Mais je ne peux pas définir les en-têtes manuellement. J'ai accès à BundleContext (et à HttpContext), mais lorsque je mets des en-têtes dans ce contexte, cela s'applique également à toutes les autres demandes.

28
stian.net

Malheureusement, il n'y a pas moyen. Vous pouvez trouver le motif dans l'implémentation interne du groupement. Dans la classe BundleHandler, ProcessRequest appelle la méthode interne ProcessRequest de la classe Bundle et elle appelle SetHeaders juste avant le HttpContext.Response.Write. Par conséquent, le cache du client est défini sur un an juste avant l'écriture de la réponse. 

Remarque: BundleHandler est une classe interne scellée: internal sealed class BundleHandler : IHttpHandler

Dans la classe 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);
    }
}

Dans la classe 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

Le comportement par défaut de la fonctionnalité de regroupement ASP.NET MVC est le suivant: si l'un des fichiers composant un regroupement change, la chaîne de requête de ce regroupement change automatiquement, à condition que vous utilisiez les éléments suivants dans le code de votre vue:

@Scripts.Render("bundle name")

Cela signifie donc que si vous avez une nouvelle version d'un fichier dans un ensemble, la prochaine fois que votre page affichera un affichage utilisant cet ensemble, elle enverra une balise de script que le navigateur client ne trouvera pas dans son cache (car la chaîne de requête est différente).

Il semble donc que cela résoudra votre problème - cela dépend de ce que vous entendez par:

et ils ne changeront pas l'URL pointant sur mon paquet chaque fois que je changerai il

4
Adam

Bien qu'il n'y ait pas de meilleur moyen de configurer la mise en cache des ensembles, vous pouvez créer un HttpModule qui identifie les demandes de l'ensemble et définit la mise en cache du contenu.

Vous avez le même effet en faisant cela sur le fichier 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);
        }
    }
2

Ce qui semble fonctionner pour moi, c'est donner un numéro de version au paquet dans la configuration du paquet, puis référencer la nouvelle version dans votre balisage.

2
Jarrod Moura

Ceci est une modification de la réponse d'Adilson, mais sans avoir à créer un HttpModule:

Dans le fichier global.asax.cs du projet MVC:

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

Transmettez un paramètre de chaîne de requête supplémentaire dans l'URL et modifiez-le chaque fois que vous souhaitez que le cache soit actualisé.

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

le dernier paramètre est personnalisé.

0
Saikat Ghosh