javascript - update - render partial view mvc 5 ajax
obligar a los navegadores a obtener los últimos archivos js y css en la aplicación asp.net (19)
A partir de la respuesta anterior modifiqué un poco el código para hacer que el ayudante también funcione con archivos CSS y añada una versión cada vez que hace algún cambio en los archivos y no solo cuando hace la compilación.
public static class HtmlHelperExtensions
{
public static MvcHtmlString IncludeVersionedJs(this HtmlHelper helper, string filename)
{
string version = GetVersion(helper, filename);
return MvcHtmlString.Create("<script type=''text/javascript'' src=''" + filename + version + "''></script>");
}
public static MvcHtmlString IncludeVersionedCss(this HtmlHelper helper, string filename)
{
string version = GetVersion(helper, filename);
return MvcHtmlString.Create("<link href=''" + filename + version + "'' type =''text/css'' rel=''stylesheet''/>");
}
private static string GetVersion(this HtmlHelper helper, string filename)
{
var context = helper.ViewContext.RequestContext.HttpContext;
var physicalPath = context.Server.MapPath(filename);
var version = "?v=" +
new System.IO.FileInfo(physicalPath).LastWriteTime
.ToString("yyyyMMddHHmmss");
context.Cache.Add(physicalPath, version, null,
DateTime.Now.AddMinutes(1), TimeSpan.Zero,
CacheItemPriority.Normal, null);
if (context.Cache[filename] == null)
{
context.Cache[filename] = version;
return version;
}
else
{
if (version != context.Cache[filename].ToString())
{
context.Cache[filename] = version;
return version;
}
return context.Cache[filename] as string;
}
}
}
Algunos navegadores guardan en caché los archivos js y css y no los refrescan a menos que los obligue a hacerlo. ¿Cuál es la forma más fácil?
Acabo de implementar esta solución que parece funcionar.
Declara una variable de versión en tu página
public string version { get; set; }
Obtenga el número de versión de la clave web.config
version = ConfigurationManager.AppSettings["versionNumber"];
En tu página aspx realiza las llamadas a JavaScript y hojas de estilo como
<script src="scripts/myjavascript.js?v=<%=version %>" type="text/javascript"></script>
<link href="styles/mystyle.css?v=<%=version %>" rel="stylesheet" type="text/css" />
Por lo tanto, si configura la versión = 1.1 de 1.0 en su web.config, su navegador descargará los últimos archivos que con suerte le ahorrarán a usted y a sus usuarios cierta frustración.
¿Hay alguna otra solución que funcione mejor o que cause problemas imprevistos para un sitio web?
ASP.NET MVC se encargará de esto si usa paquetes para su JS / CSS. Se agregará automáticamente un número de versión en forma de GUID a sus paquetes y solo actualizará este GUID cuando se actualice el paquete (también conocido como cualquiera de los archivos fuente que tengan cambios).
Esto también ayuda si tiene una tonelada de archivos JS / CSS, ya que puede mejorar en gran medida los tiempos de carga de contenido.
Aquí hay un enfoque que funciona con ASP.NET 5 / MVC 6 / vNext .
Paso 1: crea una clase para devolver la última hora de escritura del archivo, similar a otras respuestas en este hilo. Tenga en cuenta que esto requiere ASP.NET 5 (u otra) inyección de dependencia.
public class FileVersionService
{
private IHostingEnvironment _hostingEnvironment;
public FileVersionService(IHostingEnvironment hostingEnvironment)
{
_hostingEnvironment = hostingEnvironment;
}
public string GetFileVersion(string filename)
{
var path = string.Format("{0}{1}", _hostingEnvironment.WebRootPath, filename);
var fileInfo = new FileInfo(path);
var version = fileInfo.LastWriteTimeUtc.ToString("yyyyMMddhhmmssfff");
return version;
}
}
Paso 2: registre el servicio para ser inyectado dentro de startup.cs :
public void ConfigureServices(IServiceCollection services)
{
...
services.AddScoped<FileVersionService>();
...
}
Paso 3: Luego, en ASP.NET 5, es posible inyectar el servicio directamente en una vista de diseño como _Layout.cshtml de esta manera:
@inject Namespace.Here.FileVersionService fileVersionService
<!DOCTYPE html>
<html lang="en" class="@ViewBag.HtmlClass">
<head>
...
<link href="/css/[email protected]("//css//styles.css")" rel="stylesheet" />
...
</head>
<body>
...
</body>
Hay algunos toques finales que podrían hacerse para combinar mejor las rutas físicas y manejar el nombre del archivo en un estilo más consistente con la sintaxis, pero este es un punto de partida. Espero que ayude a las personas a pasar a ASP.NET 5.
Basado en la respuesta de Adam Tegan , modificado para su uso en una aplicación de formularios web.
En el código de clase .cs:
public static class FileUtility
{
public static string SetJsVersion(HttpContext context, string filename) {
string version = GetJsFileVersion(context, filename);
return filename + version;
}
private static string GetJsFileVersion(HttpContext context, string filename)
{
if (context.Cache[filename] == null)
{
string filePhysicalPath = context.Server.MapPath(filename);
string version = "?v=" + GetFileLastModifiedDateTime(context, filePhysicalPath, "yyyyMMddhhmmss");
return version;
}
else
{
return string.Empty;
}
}
public static string GetFileLastModifiedDateTime(HttpContext context, string filePath, string dateFormat)
{
return new System.IO.FileInfo(filePath).LastWriteTime.ToString(dateFormat);
}
}
En el marcado aspx:
<script type="text/javascript" src=''<%= FileUtility.SetJsVersion(Context,"/js/exampleJavaScriptFile.js") %>''></script>
Y en el HTML renderizado, aparece como
<script type="text/javascript" src=''/js/exampleJavaScriptFile.js?v=20150402021544''></script>
Curiosamente, este mismo sitio tiene problemas con el enfoque que describes en relación con algunas configuraciones de proxy, aunque debería ser a prueba de fallas.
Verifique esta discusión de Meta .
Entonces a la luz de eso, podría tener sentido no usar un parámetro GET para actualizar, sino el nombre real del archivo:
href="/css/scriptname/versionNumber.css"
aunque esto es más trabajo por hacer, ya que tendrá que crear realmente el archivo o crear una reescritura de URL para ello.
El principal problema al hacerlo de esta manera es principalmente que tendrá que recordar actualizar su número de versión en su código cada vez que realice algún cambio en sus archivos css o js.
Una forma posiblemente mejor de hacerlo es establecer un parámetro único garantizado con cada uno de sus archivos css o js, así:
<script src="scripts/myjavascript.js?_=<%=DateTime.Now.Ticks%>" type="text/javascript"></script>
<link href="styles/mystyle.css?_=<%=DateTime.Now.Ticks%>" rel="stylesheet" type="text/css" />
Esto obliga a los archivos a solicitarse del servidor cada vez, lo que también significa que su sitio no será tan eficiente en la carga de la página, ya que esos archivos nunca se almacenarán en caché, y usarán anchos de banda innecesarios cada vez.
Básicamente, si recuerda actualizar el número de versión cada vez que se realiza un cambio, puede salirse con la suya.
En ASP.NET Core (MVC 6) esto funciona de la caja a través del asistente de etiquetas asp-append-version
:
<script src="scripts/myjavascript.js" asp-append-version="true"></script>
<link href="styles/mystyle.css rel="stylesheet" asp-append-version="true" />
En base a la respuesta anterior , escribí una pequeña clase de extensión para trabajar con archivos CSS y JS:
public static class TimestampedContentExtensions
{
public static string VersionedContent(this UrlHelper helper, string contentPath)
{
var context = helper.RequestContext.HttpContext;
if (context.Cache[contentPath] == null)
{
var physicalPath = context.Server.MapPath(contentPath);
var version = @"v=" + new FileInfo(physicalPath).LastWriteTime.ToString(@"yyyyMMddHHmmss");
var translatedContentPath = helper.Content(contentPath);
var versionedContentPath =
contentPath.Contains(@"?")
? translatedContentPath + @"&" + version
: translatedContentPath + @"?" + version;
context.Cache.Add(physicalPath, version, null, DateTime.Now.AddMinutes(1), TimeSpan.Zero,
CacheItemPriority.Normal, null);
context.Cache[contentPath] = versionedContentPath;
return versionedContentPath;
}
else
{
return context.Cache[contentPath] as string;
}
}
}
En lugar de escribir algo como:
<link href="@Url.Content(@"~/Content/bootstrap.min.css")" rel="stylesheet" type="text/css" />
<script src="@Url.Content(@"~/Scripts/bootstrap.min.js")"></script>
Ahora puede escribir:
<link href="@Url.VersionedContent(@"~/Content/bootstrap.min.css")" rel="stylesheet" type="text/css" />
<script src="@Url.VersionedContent(@"~/Scripts/bootstrap.min.js")"></script>
Es decir, simplemente reemplace Url.Content
con Url.VersionedContent
.
Las URL generadas se parecen a las siguientes:
<link href="/Content/bootstrap.min.css?v=20151104105858" rel="stylesheet" type="text/css" />
<script src="/Scripts/bootstrap.min.js?v=20151029213517"></script>
Si usa la clase de extensión, es posible que desee agregar el manejo de errores en caso de que la llamada a MapPath
no funcione, ya que contentPath
no es un archivo físico.
Hay una forma incorporada en asp.net para esto: agrupar . Solo úsalo. Cada nueva versión tendrá el sufijo único "? V = XXXXXXX". En el modo de depuración, la agrupación está desactivada, para activar la configuración de creación en web.config:
<system.web>
<compilation debug="false" />
</system.web>
O agregue al método RegisterBundles (paquetes BundleCollection):
BundleTable.EnableOptimizations = true;
Por ejemplo:
BundleConfig.cs:
bundles.Add(new ScriptBundle("~/Scripts/myjavascript.js")
.Include("~/Scripts/myjavascript.js"));
bundles.Add(new StyleBundle("~/Content/mystyle.css")
.Include("~/Content/mystyle.css"));
_Layout.cshtml:
@Scripts.Render("~/Scripts/myjavascript.js")
@Styles.Render("~/Content/mystyle.css")
Hay una respuesta más simple a esto que la respuesta dada por el op en la pregunta (el enfoque es el mismo):
Defina la clave en web.config:
<add key="VersionNumber" value="06032014"/>
Realice la llamada a las aplicaciones directamente desde la página aspx:
<link href="styles/navigation.css?v=<%=ConfigurationManager.AppSettings("VersionNumber")%>" rel="stylesheet" type="text/css" />
He empleado una técnica ligeramente diferente en mi sitio aspnet MVC 4:
_ViewStart.cshtml:
@using System.Web.Caching
@using System.Web.Hosting
@{
Layout = "~/Views/Shared/_Layout.cshtml";
PageData.Add("scriptFormat", string.Format("<script src=/"{{0}}?_={0}/"></script>", GetDeployTicks()));
}
@functions
{
private static string GetDeployTicks()
{
const string cacheKey = "DeployTicks";
var returnValue = HttpRuntime.Cache[cacheKey] as string;
if (null == returnValue)
{
var absolute = HostingEnvironment.MapPath("~/Web.config");
returnValue = File.GetLastWriteTime(absolute).Ticks.ToString();
HttpRuntime.Cache.Insert(cacheKey, returnValue, new CacheDependency(absolute));
}
return returnValue;
}
}
Luego en las vistas reales:
@Scripts.RenderFormat(PageData["scriptFormat"], "~/Scripts/Search/javascriptFile.min.js")
Lo resolví atacando una última marca de tiempo modificada como un parámetro de consulta para los scripts.
Hice esto con un método de extensión y usándolo en mis archivos CSHTML. Nota: esta implementación almacena en caché la marca de tiempo durante 1 minuto, por lo que no ralentizamos demasiado el disco.
Aquí está el método de extensión:
public static class JavascriptExtension {
public static MvcHtmlString IncludeVersionedJs(this HtmlHelper helper, string filename) {
string version = GetVersion(helper, filename);
return MvcHtmlString.Create("<script type=''text/javascript'' src=''" + filename + version + "''></script>");
}
private static string GetVersion(this HtmlHelper helper, string filename)
{
var context = helper.ViewContext.RequestContext.HttpContext;
if (context.Cache[filename] == null)
{
var physicalPath = context.Server.MapPath(filename);
var version = $"?v={new System.IO.FileInfo(physicalPath).LastWriteTime.ToString("MMddHHmmss")}";
context.Cache.Add(filename, version, null,
DateTime.Now.AddMinutes(5), TimeSpan.Zero,
CacheItemPriority.Normal, null);
return version;
}
else
{
return context.Cache[filename] as string;
}
}
}
Y luego en la página CSHTML:
@Html.IncludeVersionedJs("/MyJavascriptFile.js")
En el HTML representado, esto aparece como:
<script type=''text/javascript'' src=''/MyJavascriptFile.js?20111129120000''></script>
Obtenga tiempo de archivo modificado, como se muestra a continuación
private static string GetLastWriteTimeForFile(string pathVal)
{
return System.IO.File.GetLastWriteTime(HostingEnvironment.MapPath(pathVal)).ToFileTime().ToString();
}
Agregar esto con la entrada como cadena de consulta
public static string AppendDateInFile(string pathVal)
{
var patheWithDate = new StringBuilder(pathVal);
patheWithDate.AppendFormat("{0}x={1}",
pathVal.IndexOf(''?'') >= 0 ? ''&'' : ''?'',
GetLastWriteTimeForFile(pathVal));
return patheWithDate.ToString();
}
Llamar esto desde el marcado.
MVC Extension Helper Approach
Agregar un método de extensión
namespace TNS.Portal.Helpers
{
public static class ScriptExtensions
{
public static HtmlString QueryStringScript<T>(this HtmlHelper<T> html, string path)
{
var file = html.ViewContext.HttpContext.Server.MapPath(path);
DateTime lastModified = File.GetLastWriteTime(file);
TagBuilder builder = new TagBuilder("script");
builder.Attributes["src"] = path + "?modified=" + lastModified.ToString("yyyyMMddhhmmss");
return new HtmlString(builder.ToString());
}
public static HtmlString QueryStringStylesheet<T>(this HtmlHelper<T> html, string path)
{
var file = html.ViewContext.HttpContext.Server.MapPath(path);
DateTime lastModified = File.GetLastWriteTime(file);
TagBuilder builder = new TagBuilder("link");
builder.Attributes["href"] = path + "?modified=" + lastModified.ToString("yyyyMMddhhmmss");
builder.Attributes["rel"] = "stylesheet";
return new HtmlString(builder.ToString());
}
}
}
Agregue este espacio de nombre en web.config
<system.web.webPages.razor>
<host factoryType="System.Web.Mvc.MvcWebRazorHostFactory, System.Web.Mvc, Version=5.2.3.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
<pages pageBaseType="System.Web.Mvc.WebViewPage">
<namespaces>
<add namespace="System.Web.Mvc" />
<add namespace="System.Web.Mvc.Ajax" />
<add namespace="System.Web.Mvc.Html" />
<add namespace="System.Web.Optimization"/>
<add namespace="System.Web.Routing" />
<add namespace="TNS.Portal" />
<add namespace="TNS.Portal.Helpers" />
</namespaces>
</pages>
</system.web.webPages.razor>
Úselo a la vista como
@Html.QueryStringScript("/Scripts/NPIAjaxCalls.js")
@Html.QueryStringStylesheet("/Content/StyledRadio.css")
Para páginas ASP.NET estoy usando lo siguiente
ANTES DE
<script src="/Scripts/pages/common.js" type="text/javascript"></script>
DESPUÉS (recarga forzada)
<script src="/Scripts/pages/common.js?ver<%=DateTime.Now.Ticks.ToString()%>" type="text/javascript"></script>
Agregar el DateTime.Now.Ticks funciona muy bien.
Puede anular la propiedad DefaultTagFormat de Scripts o Styles.
Scripts.DefaultTagFormat = @"<script src=""{0}?v=" + ConfigurationManager.AppSettings["pubversion"] + @"""></script>";
Styles.DefaultTagFormat = @"<link href=""{0}?v=" + ConfigurationManager.AppSettings["pubversion"] + @""" rel=""stylesheet""/>";
Sugerencias previas simplificadas y suministro de código para desarrolladores de .NET Web Forms.
Esto aceptará urls relativas ("~ /") y absolutas en la ruta del archivo al recurso.
Coloque un archivo de clase de extensiones estáticas, el siguiente:
public static string VersionedContent(this HttpContext httpContext, string virtualFilePath)
{
var physicalFilePath = httpContext.Server.MapPath(virtualFilePath);
if (httpContext.Cache[physicalFilePath] == null)
{
httpContext.Cache[physicalFilePath] = ((Page)httpContext.CurrentHandler).ResolveUrl(virtualFilePath) + (virtualFilePath.Contains("?") ? "&" : "?") + "v=" + File.GetLastWriteTime(physicalFilePath).ToString("yyyyMMddHHmmss");
}
return (string)httpContext.Cache[physicalFilePath];
}
Y luego llámalo en tu página maestra como tal:
<link type="text/css" rel="stylesheet" href="<%= Context.VersionedContent("~/styles/mystyle.css") %>" />
<script type="text/javascript" src="<%= Context.VersionedContent("~/scripts/myjavascript.js") %>"></script>
Tu solución funciona Es bastante popular de hecho.
Incluso utiliza un método similar:
<link rel="stylesheet" href="http://sstatic.net/so/all.css?v=6184">
Donde v=6184
es probablemente el número de revisión de SVN.
Utilizo una forma similar de hacer lo mismo que estás haciendo sin modificar cada página. Se agregó un evento PreRender como archivo maestro. Mantiene mi lógica en un solo lugar y es aplicable a los archivos js y css.
protected void Page_PreRender(object sender, EventArgs e)
{
HtmlLink link = null;
LiteralControl script = null;
foreach (Control c in Header.Controls)
{
//StyleSheet add version
if (c is HtmlLink)
{
link = c as HtmlLink;
if (link.Href.EndsWith(".css", StringComparison.InvariantCultureIgnoreCase))
{
link.Href += string.Format("?v={0}", ConfigurationManager.AppSettings["agVersion"]);
}
}
//Js add version
if (c is LiteralControl)
{
script = c as LiteralControl;
if (script.Text.Contains(".js"))
{
var foundIndexes = new List<int>();
for (int i = script.Text.IndexOf(".js/""); i > -1; i = script.Text.IndexOf(".js/"", i + 1))
{
foundIndexes.Add(i);
}
for (int i = foundIndexes.Count - 1; i >= 0; i--)
{
script.Text = script.Text.Insert(foundIndexes[i] + 3, string.Format("?v={0}", ConfigurationManager.AppSettings["agVersion"]));
}
}
}
}
}
<?php $rand_no = rand(10000000, 99999999)?> <script src="scripts/myjavascript.js?v=<?=$rand_no"></script>
Esto funciona para mí en todos los navegadores. Aquí he usado PHP para generar no aleatorio. Puede usar su propio idioma del lado del servidor.