Percorsi relativi di ASP.NET MVC


100

Nelle mie applicazioni, spesso devo utilizzare percorsi relativi. Ad esempio, quando faccio riferimento a JQuery, di solito lo faccio in questo modo:

<script type="text/javascript" src="../Scripts/jquery-1.2.6.js"></script>

Ora che sto effettuando la transizione a MVC, devo tenere conto dei diversi percorsi che una pagina potrebbe avere, rispetto alla radice. Questo era ovviamente un problema con la riscrittura degli URL in passato, ma sono riuscito a aggirarlo utilizzando percorsi coerenti.

Sono consapevole che la soluzione standard è quella di utilizzare percorsi assoluti come:

<script type="text/javascript" src="/Scripts/jquery-1.2.6.js"></script>

ma questo non funzionerà per me in quanto durante il ciclo di sviluppo, devo distribuire su una macchina di test su cui l'app verrà eseguita in una directory virtuale. I percorsi relativi di root non funzionano quando cambia la root. Inoltre, per motivi di manutenzione, non posso semplicemente cambiare tutti i percorsi per la durata della distribuzione del test: sarebbe un incubo in sé.

Allora qual è la soluzione migliore?

Modificare:

Poiché questa domanda sta ancora ricevendo visualizzazioni e risposte, ho pensato che potrebbe essere prudente aggiornarla per notare che a partire da Razor V2, il supporto per gli URL relativi a root è integrato, quindi puoi usare

<img src="~/Content/MyImage.jpg">

senza alcuna sintassi lato server e il motore di visualizzazione sostituisce automaticamente ~ / con qualunque sia la radice del sito corrente.

Risposte:


93

Prova questo:

<script type="text/javascript" src="<%=Url.Content("~/Scripts/jquery-1.2.6.js")%>"></script>

Oppure usa MvcContrib e fai questo:

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

1
Questo viene chiesto così spesso che dovrebbe essere una FAQ, penso che sia necessario includere un esempio nel modello.
Simon Steele

Fantastico, questo mi ha davvero tirato fuori dai guai. Grazie!
Jared

2
(So ​​che questo post è vecchio) - Non utilizzando <% = Url.Content ("~ / Scripts / jquery-1.2.6.js")%> il server esegue il rendering del percorso, mentre, se si utilizza "/ Scripts / jquery-1.2.6.js ", verrebbe servito direttamente al client, quindi, riducendo ancora una cosa che il server deve fare? Pensavo di leggere da qualche parte, più puoi evitare di avere il processo del server, meglio è, specialmente con contenuti statici come percorsi * .js? Mi rendo conto che questo utilizza risorse minime, ma se avessi un paio di centinaia / migliaia di Url.Content () nella tua app, sarebbe un paio di nanosecondi rasati, no?
Losbear

53

Sebbene sia un vecchio post, i nuovi lettori dovrebbero sapere che Razor 2 e versioni successive (impostazione predefinita in MVC4 +) risolvono completamente questo problema.

Vecchio MVC3 con Razor 1:

<a href="@Url.Content("~/Home")">Application home page</a>

Nuovo MVC4 con Razor 2 e versioni successive:

<a href="~/Home">Application home page</a>

Nessuna sintassi simile alla funzione Razor imbarazzante. Nessun tag di markup non standard.

Prefissare un percorso in qualsiasi attributo HTML con una tilde ('~') dice a Razor 2 di "farlo funzionare" sostituendo il percorso corretto. È ottimo.


Sì, e data la semplicità dell'analisi del prefisso ~ /, mi chiedo perché qualcosa di simile non sia stato integrato in ASP.NET dall'inizio.
Chris

4
Ho spesso scoperto che più semplice è il design, più ci si pensa.
Charles Burns,

1
Questa risposta è leggermente fuorviante. La sintassi pubblicata per MVC4 dipende infatti dal motore Razor. Non può utilizzare alcun markup speciale, ma solo il motore Razor v2 + gestisce la sintassi visualizzata correttamente.
Chris

1
Hai ragione, @Chris. Ho aggiornato la risposta per riflettere questo.
Charles Burns

10

Cambiamento decisivo - MVC 5

Fai attenzione a una modifica sostanziale in MVC 5 (dalle note di rilascio di MVC 5 )

Riscrittura URL e tilde (~)

Dopo l'aggiornamento ad ASP.NET Razor 3 o ASP.NET MVC 5, la notazione tilde (~) potrebbe non funzionare più correttamente se si utilizzano riscritture URL. La riscrittura URL colpisce la notazione tilde (~) in elementi HTML, come <A/>, <SCRIPT/>, <LINK/>, e di conseguenza la tilde non associa alla directory principale.

Ad esempio, se riscrivi le richieste per asp.net/content in asp.net , l'attributo href in si <A href="~/content/"/>risolve in / content / content / invece di / . Per eliminare questa modifica, è possibile impostare il contesto IIS_WasUrlRewritten su false in ogni pagina Web o in Application_BeginRequest in Global.asax.

In realtà non spiegano come farlo, ma poi ho trovato questa risposta :

Se stai utilizzando la modalità Pipeline integrata di IIS 7, prova a inserire quanto segue nel tuo Global.asax:

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

Nota: potresti voler controllare prima Request.ServerVariablescontiene effettivamente IIS_WasUrlRewrittenper essere sicuro che questo sia il tuo problema.


PS. Pensavo di avere una situazione in cui questo mi stava accadendo e stavo ottenendo src="~/content/..."URL generati nel mio HTML, ma si è scoperto che qualcosa non si aggiornava quando il mio codice veniva compilato. La modifica e il salvataggio del layout e dei file cshtml della pagina in qualche modo hanno attivato qualcosa per funzionare.


6

In ASP.NET di solito uso <img src='<%= VirtualPathUtility.ToAbsolute("~/images/logo.gif") %>' alt="Our Company Logo"/>. Non vedo perché una soluzione simile non dovrebbe funzionare in ASP.NET MVC.


6
<script src="<%=ResolveUrl("~/Scripts/jquery-1.2.6.min.js") %>" type="text/javascript"></script>

È quello che ho usato. Cambia percorso in modo che corrisponda al tuo esempio.


5

Per quello che vale, odio davvero l'idea di sporcare la mia app con i tag del server solo per risolvere i percorsi, quindi ho fatto un po 'più di ricerca e ho scelto di utilizzare qualcosa che avevo provato prima per riscrivere i collegamenti: un filtro di risposta. In questo modo, posso anteporre a tutti i percorsi assoluti un prefisso noto e sostituirlo in fase di esecuzione utilizzando l'oggetto Response.Filter e non devo preoccuparmi di tag del server non necessari. Il codice è pubblicato di seguito nel caso possa aiutare qualcun altro.

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("</html>", 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);
        }
    }
}

4

Il motore di visualizzazione Razor per MVC 3 rende ancora più semplice e pulito l'utilizzo di percorsi relativi della radice virtuale che vengono risolti correttamente in fase di esecuzione. Basta rilasciare il metodo Url.Content () nel valore dell'attributo href e si risolverà correttamente.

<a href="@Url.Content("~/Home")">Application home page</a>

1

Come Chris, non sopporto davvero di dover inserire tag lato server gonfiati all'interno del mio markup pulito solo per dire alla cosa stupida di guardare dalla radice verso l'alto. Dovrebbe essere una cosa molto semplice e ragionevole da chiedere. Ma odio anche l'idea di dover dedicarmi allo sforzo di scrivere classi C # personalizzate per fare una cosa così semplice, perché dovrei farlo? Che spreco di tempo.

Per me, ho semplicemente compromesso la "perfezione" e ho codificato il nome del percorso di root della directory virtuale all'interno dei miei riferimenti di percorso. Quindi in questo modo:

<script type="text/javascript" src="/MyProject/Scripts/jquery-1.2.6.js"></script>

Nessuna elaborazione lato server o codice C # richiesto per risolvere l'URL, che è il migliore per le prestazioni anche se so che sarebbe trascurabile a prescindere. E nessun brutto caos lato server gonfio nel mio bel markup pulito.

Dovrò solo convivere sapendo che questo è hardcoded e dovrà essere rimosso quando la cosa migra in un dominio appropriato invece di http: // MyDevServer / MyProject /

Saluti


1
Ho votato a favore per riportarti a 0. Totalmente d'accordo con i tuoi sentimenti. Sono nuovo nel web dev dopo 5 anni in puro C # e che terra disastrosa del caos degli spaghetti è tutto.
Luke Puplett

Questo sembra un compromesso accettabile fino a quando non è necessario eseguire qualcosa come la distribuzione su un'app Web nidificata. L'uso del markup del resolver risolverà questo problema ma il tuo collegamento statico verrà interrotto. Esempio: si crea localmente sul server Web integrato e quindi si
invia

Questo si interromperà in molti scenari di produzione
Oskar Duveborn il

Mi piace abbastanza questa soluzione: thoughtstuff.co.uk/2013/02/…
Dion

1

In ritardo per il gioco, ma questo post ha un riepilogo molto completo della gestione dei percorsi ASP.Net.


1

Uso un semplice metodo di supporto. Puoi usarlo facilmente nelle viste e nei controller.

Markup:

<a href=@Helper.Root()/about">About Us</a>

Metodo di supporto:

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

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.