2011-04-28 12 views
9

Estaba leyendo this stackoverflow post sobre "autoversioning" en ASP.NET MVC para archivos CSS/JS y me preguntaba cuál es la "mejor" estrategia para hacer esto.Autoversioning CSS/JS en ASP.NET MVC?

La solución proporcionada inserciones de un número de ensamblaje - lo que significa que cada vez que se publica - que va a cambiar cada archivo que es no es ideal porque si haces modificaciones a sólo 1 * .css o * .js entonces se cambiarán cada y cada archivo.

1) ¿Cómo se puede hacer solo para "archivos individuales" en lugar de usar un ensamblaje de todo el sitio usando una fecha de modificación o algo en IIS7?

2) Además, si tengo algún tipo de activo "estático" como - http://static.domain.com/js/123.js - ¿cómo puedo usar reescribir para enviar el último archivo para una solicitud si alguien ha integrado este enlace estático en su sitio?

es decir, http://static.domain.com/js/123.js es el enlace y cuando se recibe una solicitud para esto, verifique y envíe el último archivo?

+0

Honestamente, Si está agrupando, CADA archivo individual significaría entre 2 y 5 archivos minified + bundled. Creo que es una buena solución. – Worthy7

Respuesta

4

1) Use la hora de modificación de archivo en su lugar. He aquí un ejemplo:

public static string GeneratePathWithTime(string cssFileName) 
{ 
    var serverFilePath = server.MapPath("~/static/" + cssFileName); 
    var version = File.GetLastWriteTime(serverFilePath).ToString("yyyyMMddhhmmss"); 
    return string.Format("/static/{0}/{1}", version, cssFileName); 
} 

Esto generará un camino como "/static/201109231100/style.css" para "style.css" (suponiendo que el de su style.css se encuentra en el directorio static). A continuación, agregará una regla de reescritura en IIS para volver a escribir "/static/201109231100/style.css" en "/static/style.css". El número de versión solo cambiará cuando el archivo css se haya modificado y solo se aplique a los archivos modificados.

2) Puede manejar la solicitud a 123.js a través de un HttpModule y enviar el último contenido de la misma, pero no creo que pueda garantizar que la solicitud obtenga la última versión. Depende de cómo el navegador maneje su caché. Puede establecer un tiempo de caducidad anterior (por ejemplo, hace un minuto) en su encabezado de respuesta para indicar a los navegadores que siempre vuelvan a descargar el archivo, pero todo depende del navegador para decidir si volver a descargar el archivo o no. . Es por eso que necesitamos generar una ruta diferente para nuestros archivos modificados cada vez que actualizamos nuestros archivos en su pregunta 1), el navegador siempre intentará descargar el archivo si nunca se ha visitado la URL.

+2

No necesita configurar las reglas de reescritura en IIS: ¡MVC admite Navigatorly ** Routing **! –

+0

Claro. Y no solo MVC. También puede escribir un controlador de solicitud y registrarlo en web.config, o usar Global.asax. También son compatibles de forma nativa. La reescritura de IIS es solo una de las muchas soluciones. Recomiendo la reescritura de IIS porque maneja las solicitudes antes del tiempo de ejecución de .NET y se puede configurar fácilmente sin cambiar el código. – Miller

+0

Tiene razón, a partir de ASP.NET 4, el enrutamiento también está disponible en ASP.NET Core (fuera de MVC). Sin embargo, me refería a MVC ya que la pregunta está etiquetada con él. –

3

Es posible que desee echar un vistazo a Blogpost de Dean Hume MVC and the HTML5 Application Cache. En ese puesto, señala una manera elegante de manejar de forma automática por la petición de versiones, usando una biblioteca de clases de @ShirtlessKirk:

@Url.Content("~/Content/Site.css").AppendHash(Request) 
+0

Solo una palabra de advertencia para cualquiera que se tropiece con esta respuesta: esta técnica se basa en Application Cache, que parece estar en desuso: https://developer.mozilla.org/en/docs/Web/HTML/Using_the_application_cache – Orilux

3

me escribió un Asistente de URL que hace lo Cachebuster para mí.

public static string CacheBustedContent(this UrlHelper helper, string contentPath) 
{ 
    var path = string.Empty; 

    if (helper.RequestContext.HttpContext.Cache["static-resource-" + contentPath] == null) 
    { 
     var fullpath = helper.RequestContext.HttpContext.Server.MapPath(contentPath); 
     var md5 = GetMD5HashFromFile(fullpath); 
     path = helper.Content(contentPath) + "?v=" + md5; 

     helper.RequestContext.HttpContext.Cache.Add("static-resource-" + contentPath, path, null, System.Web.Caching.Cache.NoAbsoluteExpiration, new TimeSpan(24, 0, 0), System.Web.Caching.CacheItemPriority.Default, null); 
    } 
    else 
    { 
     path = helper.RequestContext.HttpContext.Cache["static-resource-" + contentPath].ToString(); 
    } 

    return path; 
} 

Se podría reemplazar el GetMD5HashFromFile() con CRC o cualquier otro tipo de llamada que genere una cadena única basada en los contenidos o de la última modificación, fecha del archivo.

El inconveniente es que se llamará siempre que se invalide la memoria caché. Y si cambia el archivo en vivo de alguna manera, pero no restablece el grupo de aplicaciones, probablemente deba tocar el archivo web.config para que vuelva a cargarse correctamente.

+1

Dicho esto, Realmente me gusta SquishIt para minificar, combinar y cachebusting tanto javascript como css. http://www.codethinked.com/squishit-the-friendly-aspnet-javascript-and-css-squisher – davewasthere

9

La forma en que resolvió este problema fue agregar la autoversión a mi proyecto MVC en el AssemblyInfo.cs archivo de este modo:

Change 
[assembly: AssemblyVersion("1.0.0.0")] 
to  
[assembly: AssemblyVersion("1.0.*")] 

Esto significa que cada vez que se genere el proyecto, que tendrá una nueva versión de ensamblado que es más alto que el anterior. Ahora tiene su número de versión único.

Entonces creó una clase UrlHelperExtension que le ayudará a obtener esta información cuando lo necesito en mis puntos de vista:

public static class UrlHelperExtensions 
{ 
    public static string ContentVersioned(this UrlHelper self, string contentPath) 
    { 
     string versionedContentPath = contentPath + "?v=" + Assembly.GetAssembly(typeof(UrlHelperExtensions)).GetName().Version.ToString(); 
     return self.Content(versionedContentPath); 
    } 
} 

Ahora puede agregar fácilmente un número de versión de su punto de vista de la siguiente manera:

<link href="@Url.ContentVersioned("style.css")" rel="stylesheet" type="text/css" /> 

Al ver su fuente de la página que ahora tendrá algo que se parece

<link href="style.css?v=1.0.4809.30029" rel="stylesheet" type="text/css" /> 
+0

obtengo un error ** No hay sobrecarga para el método 'ContentVersioned' toma 1 argumentos ** soy haciendo Anjyr

+0

¡Gran solución! – mrpotocnik

+0

@Anjyr basado en la sugerencia anterior y suponiendo que tiene la extensión ContentVersioned como la anterior, cambie src='~/Scripts/demoproject/@FileVersioning.ContentVersioned ("custom.js") ' a src =' @ Url.ContentVersioned ("~/full/path/to/custom.js") ' – mrpotocnik

2

ACTUALIZACIÓN: La versión anterior no funcionaba en Azure, he simplificado y corregido a continuación. (Tenga en cuenta que para que esto funcione en modo de desarrollo con IIS Express, necesitará instalar URL Rewrite 2.0 desde Microsoft http://www.iis.net/downloads/microsoft/url-rewrite - usa el instalador WebPi, asegúrese de cerrar Visual Studio primero)

Si desea cambiar los nombres reales de los archivos, en lugar de agregar una cadena de consulta (que es ignorada por algunos proxies/navegadores para archivos estáticos) Puede seguir los siguientes pasos: (Sé que esta es una publicación anterior, pero la encontré mientras desarrollaba una solución :

Cómo hacerlo: Auto-incremento de la versión de montaje cada vez que se genere el proyecto, y usar ese número para un archivo estático enrutado en la re específica fuentes que le gustaría mantener actualizado. (Así que something.js se incluye como algo.v1234.js con 1234 cambiando automáticamente cada vez que se construye el proyecto) - También agregué algunas funcionalidades adicionales para asegurar que los archivos .min.js se usen en producción y se usen archivos regulares.js al depurar (estoy usando WebGrease para automatizar el proceso de minify) Una cosa buena de esta solución es que funciona tanto en modo local/dev como en producción. (Estoy usando Visual Studio 2015/Net 4.6, pero creo que esto va a funcionar en las versiones anteriores, así

. Paso 1: Habilitar incremento automático en el conjunto cuando se construyó En el archivo AssemblyInfo.cs (que se encuentra en la sección "propiedades" de su proyecto de cambiar las siguientes líneas:

[assembly: AssemblyVersion("1.0.0.0")] 
[assembly: AssemblyFileVersion("1.0.0.0")] 

a

[assembly: AssemblyVersion("1.0.*")] 
//[assembly: AssemblyFileVersion("1.0.0.0")] 

Paso 2: Configurar rew url rito en el web.config para los archivos con las babosas versión incrustada (ver paso 3)

En web.config (el principal para el proyecto) añadir las siguientes reglas en la sección <system.webServer> lo pongo directamente después de la etiqueta </httpProtocol> final.

<rewrite> 
    <rules> 
    <rule name="static-autoversion"> 
     <match url="^(.*)([.]v[0-9]+)([.](js|css))$" /> 
     <action type="Rewrite" url="{R:1}{R:3}" /> 
    </rule> 
    <rule name="static-autoversion-min"> 
     <match url="^(.*)([.]v[0-9]+)([.]min[.](js|css))$" /> 
     <action type="Rewrite" url="{R:1}{R:3}" /> 
    </rule> 
    </rules> 
</rewrite> 

Paso 3: variables de aplicación de configuración para leer su versión actual asamblea y crean babosas versión en sus js y css.

en Global.asax.cs (que se encuentra en la raíz del proyecto) añadir el siguiente código a Application_Start protegida vacío() (después de las líneas de Registro)

  // setup application variables to write versions in razor (including .min extension when not debugging) 
      string addMin = ".min"; 
      if (System.Diagnostics.Debugger.IsAttached) { addMin = ""; } // don't use minified files when executing locally 
      Application["JSVer"] = "v" + System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString().Replace('.','0') + addMin + ".js"; 
      Application["CSSVer"] = "v" + System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString().Replace('.', '0') + addMin + ".css"; 

Paso 4: Cambiar enlaces src en la maquinilla de afeitar considera que el uso de las variables de la aplicación que creó en Global.asax.cs

@HttpContext.Current.Application["CSSVer"] 
@HttpContext.Current.Application["JSVer"] 

por ejemplo, en mi _Layout.cshtml, en mi sección de la cabeza, tengo el siguiente bloque de código de hojas de estilo:

<!-- Load all stylesheets --> 
<link rel='stylesheet' href='https://fontastic.s3.amazonaws.com/8NNKTYdfdJLQS3D4kHqhLT/icons.css' /> 
<link rel='stylesheet' href='/Content/css/[email protected]["CSSVer"]' /> 
<link rel='stylesheet' media='(min-width: 700px)' href='/Content/css/[email protected]["CSSVer"]' /> 
<link rel='stylesheet' media='(min-width: 700px)' href='/Content/css/[email protected]["CSSVer"]' /> 
@RenderSection("PageCSS", required: false) 

un par de cosas de aviso aquí: 1) no hay ninguna extensión en el archivo. 2) tampoco hay .min. Ambos son manejados por el código en Global.asax.cs

Del mismo modo, (también en _Layout.cs) en mi sección javascript: Tengo el siguiente código:

<script src="~/Scripts/all3bnd100.min.js" type="text/javascript"></script> 
<script src="~/Scripts/[email protected]["JSVer"]" type="text/javascript"></script> 
@RenderSection("scripts", required: false) 

El primer archivo es una paquete de todas las bibliotecas de terceros que he creado manualmente con WebGrease. Si agrego o cambio alguno de los archivos en el paquete (lo que es raro), renombro manualmente el archivo a all3bnd101.min.js, all3bnd102.min.js, etc ... Este archivo no coincide con el controlador de reescritura, por lo que permanecerá almacenado en caché en el navegador del cliente hasta que vuelva a agrupar/cambiar el nombre manualmente.

El segundo archivo es ui.js (que se escribirá como ui.v12345123.js o ui.v12345123.min.js dependiendo de si se está ejecutando en modo de depuración o no) Esto se manejará/reescribirá. (Se puede establecer un punto de interrupción en Application_OnBeginRequest de Global.asax.cs para ver que funcione)

discusión completa sobre este tema en: Simplified Auto-Versioning of Javascript/CSS in ASP.NET MVC 5 to stop caching issues (works in Azure and Locally) With or Without URL Rewrite(incluyendo una forma de hacerlo sin reescritura de URL)

+0

simple y limpio ... gracias richard. – Bharat