ASP.NET MVC相対パス


100

私のアプリケーションでは、多くの場合、相対パスを使用する必要があります。たとえば、JQueryを参照する場合、通常は次のようにします。

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

MVCへの移行を行っているので、ルートに対してページが持つ可能性のあるさまざまなパスを考慮する必要があります。もちろん、これは過去のURL書き換えの問題でしたが、一貫したパスを使用して回避することができました。

私は標準的な解決策が次のような絶対パスを使用することであることを認識しています:

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

しかし、開発サイクルの間は、アプリが仮想ディレクトリで実行されるテストマシンに展開する必要があるため、これは私には機能しません。ルートが変更された場合、ルートの相対パスは機能しません。また、メンテナンス上の理由から、テストの展開中にすべてのパスを単純に変更することはできません。それ自体が悪夢です。

それで、最善の解決策は何ですか?

編集:

この質問はまだ見解と回答を受け取っているので、Razor V2の時点ではルート相対URLのサポートが組み込まれているので、

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

サーバー側の構文なしで、ビューエンジンは自動的に〜/を現在のサイトルートが何であるかに置き換えます。

回答:


93

これを試して:

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

または、MvcContribを使用してこれを実行します。

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

1
これはよく聞かれる質問ですが、テンプレートに例を含める必要があると思います。
Simon Steele

すごい、これは本当に私を束縛から外しました。ありがとう!
Jared、

2
(私はこの投稿が古いことを知っています)-<%= Url.Content( "〜/ Scripts / jquery-1.2.6.js")%>を使用しないと、サーバーはパスをレンダリングしますが、 "/ Scripts / jquery-1.2.6.js」の場合、クライアントに直接提供されるため、サーバーが実行する必要のある処理が1つ少なくなりますか?特に* .jsパスのような静的コンテンツでは、サーバープロセスを回避できるほど、どこかで読んだ方がいいと思いました。私はこれが最小限のリソースを使用することを理解していますが、アプリに数百/数千のUrl.Content()がある場合、それは数ナノ秒削り取られますね?
Losbear

52

古い投稿ですが、新しい読者はRazor 2以降(MVC4 +のデフォルト)がこの問題を完全に解決することを知っているはずです。

Razor 1を備えた古いMVC3:

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

Razor 2以降の新しいMVC4:

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

厄介なRazor関数のような構文はありません。非標準のマークアップタグはありません。

チルド( '〜')を使用してHTML属性のパスにプレフィックスを付けると、Razor 2に正しいパスを代入して「機能させる」ように指示します。それは素晴らしい。


はい、そして〜/プレフィックスを解析する単純さを考えると、なぜこのようなものが最初からASP.NETに組み込まれなかったのでしょうか。
Chris

4
私は、デザインがシンプルであるほど、多くのことを考えていることがよくあります。
Charles Burns

1
この答えは少し誤解を招くものです。MVC4に投稿された構文は、実際にはかみそりエンジンに依存しています。特別なマークアップを使用することはできませんが、Razor v2 +エンジンのみが正しく表示される構文を処理します。
Chris

1
その通りです、@ Chris。これを反映するように答えを更新しました。
Charles Burns

10

重大な変更-MVC 5

MVC 5の重大な変更変更に注意してくださいMVC 5リリースノートから

URLの書き換えとティルダ(〜)

ASP.NET Razor 3またはASP.NET MVC 5にアップグレードした後、URL書き換えを使用している場合、チルダ(〜)表記が正しく機能しなくなる可能性があります。URL書き換えは、以下のようなHTML要素にチルダ(〜)の表記に影響を与えない <A/><SCRIPT/><LINK/>、結果としてチルダはもはやルートディレクトリにマップします。

たとえば、あなたがのために要求書き換える場合asp.net/contentasp.netを、のhref属性 <A href="~/content/"/>に解決さ /コンテンツ/コンテンツ/の代わりに/。この変更を抑制するには、各WebページまたはGlobal.asaxのApplication_BeginRequestIIS_WasUrlRewrittenコンテキストをfalseに 設定します。

彼らは実際にそれを行う方法を説明していませんが、それから私はこの答えを見つけました:

IIS 7統合パイプラインモードで実行している場合は、次のコードをに入れてみてくださいGlobal.asax

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

注:これがRequest.ServerVariables実際にIIS_WasUrlRewritten問題であることを確認するために、実際に内容を最初に確認することをお勧めします。


PS。これが起こっている状況でsrc="~/content/..."、HTMLにURLが生成されていると思いましたが、コードをコンパイルしているときに、何かが更新されないことがわかりました。レイアウトとページのcshtmlファイルを編集して再保存すると、なんらかの動作が発生しました。


6

ASP.NETでは通常、を使用します<img src='<%= VirtualPathUtility.ToAbsolute("~/images/logo.gif") %>' alt="Our Company Logo"/>。同様のソリューションがASP.NET MVCで機能しない理由がわかりません。


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

私が使用したものです。例に合わせてパスを変更します。


5

それだけの価値があるので、パスを解決するためだけにサーバータグでアプリを散らかすのは嫌いなので、もう少し調べて、リンクを書き換えるために以前に試したもの、つまり応答フィルターを使用することにしました。このようにして、すべての絶対パスに既知のプレフィックスをプレフィックスとして付け、Response.Filterオブジェクトを使用して実行時に置き換えることができ、不要なサーバータグを気にする必要がありません。他の人を助けるために、コードは以下に掲載されています。

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

MVC 3のRazorビューエンジンを使用すると、実行時に適切に解決される仮想ルート相対パスをより簡単かつクリーンに使用できます。Url.Content()メソッドをhref属性値にドロップするだけで、適切に解決されます。

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

1

Chrisと同様に、純粋なルートを上から見るように言うだけのために、クリーンなマークアップ内に肥大化したサーバー側タグを配置しなくてはなりません。それは非常にシンプルで合理的なものでなければなりません。しかし、私はまた、このような単純なことを行うためにカスタムC#クラスを作成する努力に行かなければならないという考えも嫌いです。なぜ私はそうしなければならないのですか?時間の無駄だ。

私にとっては、「完全性」を妥協し、仮想ディレクトリのルートパス名をパス参照内にハードコードしました。このように:

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

URLを解決するためにサーバー側の処理やC#コードは必要ありません。これはパフォーマンスに最適ですが、関係なく無視できることはわかっています。そして、すてきなクリーンマークアップでは、肥大化した醜いサーバー側の混乱はありません。

これはハードコードされており、http:// MyDevServer / MyProject /の代わりに適切なドメインに移行するときに削除する必要があることを知っておく必要があります。

乾杯


1
私はあなたを0に戻すために投票しました。あなたの感情に完全に同意します。私は純粋なC#で5年間働いた後、web開発に初めて参加しました。
ルークプレット

ネストされたWebアプリへのデプロイなどを行う必要があるまで、これは許容できる妥協のようです。リゾルバーマークアップを使用すると、これは修正されますが、静的リンクは壊れます。例:組み込みのWebサーバーに対してローカルでビルドし、アプリをdomain.com/myNewWebAppにプッシュします
plyawn

これは多くの生産シナリオで壊れます
Oskar Duveborn 2013

私はかなりこのソリューションのように:thoughtstuff.co.uk/2013/02/...
ディオン


1

単純なヘルパーメソッドを使用します。ビューとコントローラーで簡単に使用できます。

マークアップ:

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

ヘルパーメソッド:

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

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.