Rutas relativas ASP.NET MVC

En mis aplicaciones, a menudo tengo que usar rutas relativas. Por ejemplo, cuando hago referencia a JQuery, generalmente lo hago así:

 

Ahora que estoy haciendo la transición a MVC, necesito dar cuenta de las diferentes rutas que puede tener una página, en relación con la raíz. Esto fue, por supuesto, un problema con la reescritura de URL en el pasado, pero me las arreglé para solucionarlo mediante el uso de rutas consistentes.

Soy consciente de que la solución estándar es usar rutas absolutas como:

  

pero esto no funcionará para mí, ya que durante el ciclo de desarrollo, tengo que implementarlo en una máquina de prueba en la que la aplicación se ejecutará en un directorio virtual. Las rutas relativas de raíz no funcionan cuando la raíz cambia. Además, por razones de mantenimiento, no puedo simplemente cambiar todas las rutas durante la implementación de la prueba, sería una pesadilla en sí misma.

Entonces, ¿cuál es la mejor solución?

Editar:

Dado que esta pregunta aún recibe vistas y respuestas, pensé que sería prudente actualizarla para observar que a partir de Razor V2, el soporte para las URL relativas a la raíz está integrado, por lo que puede usar

  

sin ninguna syntax del lado del servidor, y el motor de vista reemplaza automáticamente ~ / con lo que sea la raíz del sitio actual.

Prueba esto:

  

O use MvcContrib y haga esto:

 <%=Html.ScriptInclude("~/Content/Script/jquery.1.2.6.js")%> 

Mientras que una publicación anterior, los lectores nuevos deben saber que Razor 2 y posterior (predeterminado en MVC4 +) resuelve completamente este problema.

Viejo MVC3 con Razor 1:

 Application home page 

Nuevo MVC4 con Razor 2 y posterior:

 Application home page 

Sin una incómoda syntax similar a la función Razor. No hay tags de marcado no estándar.

Prefijar una ruta en cualquier atributo HTML con una tilde (‘~’) le dice a Razor 2 que “simplemente lo haga funcionar” sustituyendo la ruta correcta. Es genial.

Rompiendo el cambio – MVC 5

Tenga cuidado con un cambio de cambio de ruptura en MVC 5 (de las notas de la versión MVC 5 )

Url Rewrite and Tilde (~)

Después de actualizar a ASP.NET Razor 3 o ASP.NET MVC 5, la notación tilde (~) puede dejar de funcionar correctamente si está utilizando reescrituras de URL. La reescritura de URL afecta a la notación de tilde (~) en elementos HTML como , , , y como resultado, la tilde ya no se correlaciona con el directorio raíz.

Por ejemplo, si reescribe solicitudes para asp.net/content a asp.net , el atributo href en resuelve en / content / content / en lugar de / . Para suprimir este cambio, puede establecer el contexto IIS_WasUrlRewritten en falso en cada página web o en Application_BeginRequest en Global.asax.

En realidad, no explican cómo hacerlo, pero luego encontré esta respuesta :

Si está ejecutando en el modo Integrated Pipeline de IIS 7 intente poner lo siguiente en su Global.asax :

  protected void Application_BeginRequest(object sender, EventArgs e) { Request.ServerVariables.Remove("IIS_WasUrlRewritten"); } 

Nota: Es posible que desee comprobar Request.ServerVariables realidad contiene IIS_WasUrlRewritten primero para asegurarse de que este es su problema.


PD. Pensé que tenía una situación en la que me estaba pasando esto y estaba obteniendo src="~/content/..." URLS generadas en mi HTML, pero resultó que algo no era refrescante cuando mi código estaba siendo comstackdo. Al editar y volver a guardar los archivos Layout y page cshtml de alguna manera se activó algo.

En ASP.NET suelo utilizar Our Company Logo . No veo por qué una solución similar no debería funcionar en ASP.NET MVC.

  

Es lo que usé Cambie la ruta para que coincida con su ejemplo.

Por lo que vale, realmente odio la idea de ensuciar mi aplicación con tags de servidor solo para resolver rutas, así que investigué un poco más y opté por usar algo que había intentado antes para reescribir enlaces: un filtro de respuesta. De esta manera, puedo prefijar todas las rutas absolutas con un prefijo conocido y reemplazarlo en tiempo de ejecución utilizando el objeto Response.Filter y no tener que preocuparme por las tags de servidor innecesarias. El código se publica a continuación en caso de que ayude a alguien más.

 using System; using System.IO; using System.Text; using System.Text.RegularExpressions; using System.Web; namespace Demo { public class PathRewriter : Stream { Stream filter; HttpContext context; object writeLock = new object(); StringBuilder sb = new StringBuilder(); Regex eofTag = new Regex("", RegexOptions.IgnoreCase | RegexOptions.Compiled); Regex rootTag = new Regex("/_AppRoot_", RegexOptions.IgnoreCase | RegexOptions.Compiled); public PathRewriter(Stream filter, HttpContext context) { this.filter = filter; this.context = context; } public override void Write(byte[] buffer, int offset, int count) { string temp; lock (writeLock) { temp = Encoding.UTF8.GetString(buffer, offset, count); sb.Append(temp); if (eofTag.IsMatch(temp)) RewritePaths(); } } public void RewritePaths() { byte[] buffer; string temp; string root; temp = sb.ToString(); root = context.Request.ApplicationPath; if (root == "/") root = ""; temp = rootTag.Replace(temp, root); buffer = Encoding.UTF8.GetBytes(temp); filter.Write(buffer, 0, buffer.Length); } public override bool CanRead { get { return true; } } public override bool CanSeek { get { return filter.CanSeek; } } public override bool CanWrite { get { return true; } } public override void Flush() { return; } public override long Length { get { return Encoding.UTF8.GetBytes(sb.ToString()).Length; } } public override long Position { get { return filter.Position; } set { filter.Position = value; } } public override int Read(byte[] buffer, int offset, int count) { return filter.Read(buffer, offset, count); } public override long Seek(long offset, SeekOrigin origin) { return filter.Seek(offset, origin); } public override void SetLength(long value) { throw new NotImplementedException(); } } public class PathFilterModule : IHttpModule { public void Dispose() { return; } public void Init(HttpApplication context) { context.ReleaseRequestState += new EventHandler(context_ReleaseRequestState); } void context_ReleaseRequestState(object sender, EventArgs e) { HttpApplication app = sender as HttpApplication; if (app.Response.ContentType == "text/html") app.Response.Filter = new PathRewriter(app.Response.Filter, app.Context); } } } 

El motor Razor view para MVC 3 facilita y simplifica aún más el uso de rutas relativas de raíz virtual que se resuelven adecuadamente en tiempo de ejecución. Simplemente suelte el método Url.Content () en el valor del atributo href y se resolverá correctamente.

 Application home page 

Tarde en el juego, pero esta publicación tiene un resumen muy completo del manejo de las rutas ASP.Net.

Yo uso un método simple de ayuda. Puede usarlo fácilmente en las Vistas y Controladores.

Margen:

 About Us 

Método de ayuda:

 public static string Root() { if (HttpContext.Current.Request.Url.Host == "localhost") { return ""; } else { return "/productionroot"; } } 

Fui con un enfoque diferente basado en una publicación SO similar, pero con mucho menos código …

http://a.shinynew.me/post/6042784654/relative-paths-in-asp-net-mvc-javascript

Al igual que Chris, realmente no soporto tener que poner tags hinchadas en el lado del servidor dentro de mi marcado limpio simplemente para decirle a la estúpida cosa que mire desde la raíz hacia arriba. Eso debería ser algo muy simple y razonable de pedir. Pero también odio la idea de tener que esforzarme por escribir cualquier clase personalizada de C # para hacer algo tan simple, ¿por qué debería hacerlo? Que perdida de tiempo.

Para mí, simplemente me comprometí con la “perfección” y codifiqué el nombre de la ruta raíz del directorio virtual dentro de las referencias de mi ruta. Así que así:

  

No se requiere procesamiento en el servidor o código C # para resolver la URL, lo cual es mejor para el rendimiento, aunque sé que sería insignificante. Y no hay un caos feo e hinchado en el lado del servidor en mi buen marcado limpio.

Tendré que vivir sabiendo que esto está codificado y que será necesario eliminarlo cuando la cosa migre a un dominio adecuado en lugar de http: // MyDevServer / MyProject /

Aclamaciones