Relative ASP.NET MVC-Pfade

100

In meinen Anwendungen muss ich häufig relative Pfade verwenden. Wenn ich zum Beispiel auf JQuery verweise, mache ich das normalerweise so:

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

Jetzt, da ich den Übergang zu MVC mache, muss ich die unterschiedlichen Pfade berücksichtigen, die eine Seite im Verhältnis zum Stamm haben kann. Dies war in der Vergangenheit natürlich ein Problem beim Umschreiben von URLs, aber ich habe es geschafft, es mithilfe konsistenter Pfade zu umgehen.

Mir ist bewusst, dass die Standardlösung darin besteht, absolute Pfade zu verwenden, wie zum Beispiel:

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

Dies funktioniert jedoch nicht für mich, da ich während des Entwicklungszyklus auf einem Testcomputer bereitstellen muss, auf dem die App in einem virtuellen Verzeichnis ausgeführt wird. Relative Root-Pfade funktionieren nicht, wenn sich der Root ändert. Außerdem kann ich aus Wartungsgründen nicht einfach alle Pfade für die Dauer der Bereitstellung des Tests ändern - das wäre an sich schon ein Albtraum.

Was ist die beste Lösung?

Bearbeiten:

Da diese Frage immer noch Ansichten und Antworten erhält, hielt ich es für ratsam, sie zu aktualisieren, um festzustellen, dass ab Razor V2 die Unterstützung für root-relative URLs integriert ist, sodass Sie sie verwenden können

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

ohne serverseitige Syntax, und die Ansichts-Engine ersetzt ~ / automatisch durch das aktuelle Site-Stammverzeichnis.

Chris
quelle

Antworten:

93

Versuche dies:

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

Oder verwenden Sie MvcContrib und tun Sie dies:

<%=Html.ScriptInclude("~/Content/Script/jquery.1.2.6.js")%>
Tim Scott
quelle
1
Dies wird so oft gefragt, dass es sich um eine FAQ handeln sollte. Ich denke, sie müssen ein Beispiel in die Vorlage aufnehmen.
Simon Steele
Genial, das hat mich wirklich aus der Klemme gebracht. Vielen Dank!
Jared
2
(Ich weiß, dass dieser Beitrag alt ist) - Wenn Sie <% = Url.Content ("~ / Scripts / jquery-1.2.6.js")% nicht verwenden, wird der Server den Pfad rendern, während Sie "/" verwenden Scripts / jquery-1.2.6.js ", wird es nur direkt an den Client geliefert, wodurch eine weitere Aufgabe des Servers reduziert wird? Ich dachte, ich lese irgendwo, je mehr Sie vermeiden können, dass der Server verarbeitet wird, desto besser - insbesondere bei statischen Inhalten wie * .js Pfaden? Mir ist klar, dass dies nur minimale Ressourcen verbraucht, aber wenn Sie ein paar hunderttausend Url.Content () in Ihrer App hatten, sind das ein paar Nanosekunden, die rasiert wurden, nicht wahr?
Losbear
52

Während eines alten Beitrags sollten neue Leser wissen, dass Razor 2 und höher (Standard in MVC4 +) dieses Problem vollständig löst.

Alte MVC3 mit Rasiermesser 1:

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

Neue MVC4 mit Razor 2 und höher:

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

Keine umständliche Razor-Funktions-ähnliche Syntax. Keine nicht standardmäßigen Markup-Tags.

Wenn Sie einem Pfad in einem HTML-Attribut eine Tilde ('~') voranstellen, wird Razor 2 angewiesen, "einfach zum Laufen zu bringen", indem Sie den richtigen Pfad ersetzen. Es ist toll.

Charles Burns
quelle
Ja, und angesichts der Einfachheit, das Präfix ~ / zu analysieren, frage ich mich, warum so etwas nicht von Anfang an in ASP.NET integriert war.
Chris
4
Ich habe oft festgestellt, dass je einfacher das Design ist, desto mehr Gedanken wurden darauf verwendet.
Charles Burns
1
Diese Antwort ist leicht irreführend. Die für MVC4 veröffentlichte Syntax hängt tatsächlich von der Rasiermaschine ab. Möglicherweise wird kein spezielles Markup verwendet, aber nur die Razor v2 + Engine verarbeitet die angezeigte Syntax korrekt.
Chris
1
Du hast recht, @Chris. Ich habe die Antwort aktualisiert, um dies widerzuspiegeln.
Charles Burns
10

Breaking Change - MVC 5

Achten Sie auf eine grundlegende Änderung in MVC 5 (aus den Versionshinweisen zu MVC 5 ).

Url Rewrite und Tilde (~)

Nach dem Upgrade auf ASP.NET Razor 3 oder ASP.NET MVC 5 funktioniert die Tilde-Notation (~) möglicherweise nicht mehr ordnungsgemäß, wenn Sie URL-Umschreibungen verwenden. Die URL - Rewrite wirkt sich auf die Tilde (~) Notation in HTML - Elemente wie <A/>, <SCRIPT/>,<LINK/> und als Ergebnis wird die Tilde nicht mehr Karten in das Stammverzeichnis.

Zum Beispiel, wenn Sie Anforderungen für neu schreiben asp.net/content zu asp.net , das href - Attribut in <A href="~/content/"/>Entschlüssen zu / content / content / statt / . Um diese Änderung zu unterdrücken, können Sie den Kontext IIS_WasUrlRewritten auf jeder Webseite oder in Application_BeginRequest in Global.asax auf false setzen.

Sie erklären nicht wirklich, wie es geht, aber dann fand ich diese Antwort :

Wenn Sie im integrierten Pipeline-Modus von IIS 7 ausgeführt werden, geben Sie Folgendes in Ihre Datei ein Global.asax:

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

Hinweis: Möglicherweise möchten Sie zuerst überprüfen, ob Request.ServerVariablestatsächlich enthalten ist IIS_WasUrlRewritten, um sicherzustellen, dass dies Ihr Problem ist.


PS. Ich dachte, ich hätte eine Situation, in der mir dies passiert ist, und ich habe src="~/content/..."URLs in meinen HTML-Code generiert - aber es stellte sich heraus, dass beim Kompilieren meines Codes etwas nicht aktualisiert wurde. Das Bearbeiten und erneute Speichern der Layout- und Seiten-cshtml-Dateien hat irgendwie dazu geführt, dass etwas funktioniert.

Simon_Weaver
quelle
6

In ASP.NET verwende ich normalerweise <img src='<%= VirtualPathUtility.ToAbsolute("~/images/logo.gif") %>' alt="Our Company Logo"/>. Ich verstehe nicht, warum eine ähnliche Lösung in ASP.NET MVC nicht funktionieren sollte.

kͩeͣmͮpͥ ͩ
quelle
6
<script src="<%=ResolveUrl("~/Scripts/jquery-1.2.6.min.js") %>" type="text/javascript"></script>

Ist was ich benutzt habe. Ändern Sie den Pfad entsprechend Ihrem Beispiel.

Jesper Palm
quelle
5

Für das, was es wert ist, hasse ich die Idee, meine App mit Server-Tags zu verunreinigen, nur um Pfade aufzulösen. Deshalb habe ich ein bisschen mehr recherchiert und mich dafür entschieden, etwas zu verwenden, das ich zuvor versucht hatte, um Links neu zu schreiben - einen Antwortfilter. Auf diese Weise kann ich allen absoluten Pfaden ein bekanntes Präfix voranstellen und es zur Laufzeit mithilfe des Response.Filter-Objekts ersetzen, ohne mich um unnötige Server-Tags kümmern zu müssen. Der Code ist unten angegeben, falls er jemand anderem hilft.

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);
        }
    }
}
Chris
quelle
4

Die Razor View Engine für MVC 3 macht es noch einfacher und sauberer, relative Pfade mit virtuellem Stamm zu verwenden, die zur Laufzeit ordnungsgemäß aufgelöst werden. Fügen Sie einfach die Url.Content () -Methode in den href-Attributwert ein, und sie wird ordnungsgemäß aufgelöst.

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

Wie Chris kann ich es wirklich nicht ertragen, aufgeblähte serverseitige Tags in mein sauberes Markup einfügen zu müssen, nur um der dummen Sache zu sagen, dass sie von der Wurzel nach oben schauen soll. Das sollte eine sehr einfache und vernünftige Frage sein. Aber ich hasse auch die Idee, sich die Mühe machen zu müssen, benutzerdefinierte C # -Klassen zu schreiben, um so eine einfache Sache zu machen. Warum sollte ich das tun müssen? Was für eine Zeitverschwendung.

Für mich habe ich einfach Kompromisse bei der "Perfektion" eingegangen und den Stammpfadnamen des virtuellen Verzeichnisses in meinen Pfadreferenzen fest codiert. Also so:

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

Keine serverseitige Verarbeitung oder C # -Code erforderlich, um die URL aufzulösen. Dies ist für die Leistung am besten, obwohl ich weiß, dass dies unabhängig davon vernachlässigbar wäre. Und kein aufgeblähtes hässliches Chaos auf der Serverseite in meinem schönen, sauberen Markup.

Ich muss nur damit leben, dass ich weiß, dass dies fest codiert ist und entfernt werden muss, wenn das Objekt auf eine richtige Domain anstatt auf http: // MyDevServer / MyProject / migriert wird.

Prost

Aaron
quelle
1
Ich habe dafür gestimmt, Sie wieder auf 0 zu bringen. Stimmen Sie Ihren Ansichten voll und ganz zu. Ich bin neu bei Web Dev nach 5 Jahren in reinem C # und was für ein katastrophales Land des Spaghetti-Chaos das alles ist.
Luke Puplett
Dies scheint ein akzeptabler Kompromiss zu sein, bis Sie beispielsweise eine Bereitstellung für eine verschachtelte Web-App durchführen müssen. Die Verwendung des Resolver-Markups behebt dies, aber Ihre statische Verbindung wird unterbrochen. Beispiel: Sie erstellen lokal auf dem integrierten Webserver und senden die App dann an domain.com/myNewWebApp
plyawn
Dies wird in vielen Produktionsszenarien brechen
Oskar Duveborn
Ich mag diese Lösung sehr : oughtstuff.co.uk/2013/02/…
Dion
1

Spät zum Spiel, aber dieser Beitrag enthält eine sehr vollständige Zusammenfassung der Behandlung von ASP.Net-Pfaden.

Plyawn
quelle
1

Ich benutze eine einfache Hilfsmethode. Sie können es einfach in den Ansichten und Controllern verwenden.

Markup:

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

Hilfsmethode:

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