Jalur Relatif ASP.NET MVC

100

Dalam aplikasi saya, saya sering harus menggunakan jalur relatif. Misalnya, ketika saya mereferensikan JQuery, saya biasanya melakukannya seperti ini:

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

Sekarang saya melakukan transisi ke MVC, saya perlu memperhitungkan jalur berbeda yang mungkin dimiliki halaman, relatif terhadap root. Ini tentu saja merupakan masalah dengan penulisan ulang URL di masa lalu, tetapi saya berhasil mengatasinya dengan menggunakan jalur yang konsisten.

Saya sadar bahwa solusi standarnya adalah menggunakan jalur absolut seperti:

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

tetapi ini tidak akan berfungsi untuk saya karena selama siklus pengembangan, saya harus menerapkan ke mesin uji tempat aplikasi akan berjalan dalam direktori virtual. Jalur relatif root tidak berfungsi saat root berubah. Juga, untuk alasan pemeliharaan, saya tidak bisa begitu saja mengubah semua jalur selama penerapan pengujian - itu akan menjadi mimpi buruk itu sendiri.

Jadi apa solusi terbaiknya?

Edit:

Karena pertanyaan ini masih menerima pandangan dan jawaban, saya pikir mungkin lebih bijaksana untuk memperbaruinya untuk dicatat bahwa pada Razor V2, dukungan untuk url root-relative sudah dimasukkan, sehingga Anda dapat menggunakan

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

tanpa sintaks sisi server, dan mesin tampilan secara otomatis menggantikan ~ / dengan akar situs apa pun saat ini.

Chris
sumber

Jawaban:

93

Coba ini:

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

Atau gunakan MvcContrib dan lakukan ini:

<%=Html.ScriptInclude("~/Content/Script/jquery.1.2.6.js")%>
Tim Scott
sumber
1
Ini sering ditanyakan sehingga harus menjadi FAQ, saya pikir mereka perlu menyertakan contoh di template.
Simon Steele
Luar biasa, ini benar-benar membuat saya lepas kendali. Terima kasih!
Jared
2
(Saya tahu posting ini sudah lama) - Tidak menggunakan <% = Url.Content ("~ / Scripts / jquery-1.2.6.js")%> membuat server merender jalur, sedangkan, jika Anda menggunakan "/ Scripts / jquery-1.2.6.js ", itu hanya akan disajikan langsung ke klien, oleh karena itu, mengurangi satu hal lagi yang harus dilakukan server? Saya pikir saya membaca semakin banyak Anda dapat menghindari proses server, semakin baik - terutama dengan konten statis seperti jalur * .js? Saya menyadari ini menggunakan sumber daya minimal, tetapi jika Anda memiliki beberapa ratus / ribu Url.Content () di aplikasi Anda, itu hanya beberapa nanodetik, bukan?
Losbear
53

Sementara posting lama, pembaca baru harus tahu bahwa Razor 2 dan yang lebih baru (default di MVC4 +) sepenuhnya menyelesaikan masalah ini.

MVC3 lama dengan Razor 1:

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

MVC4 baru dengan Razor 2 dan lebih baru:

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

Tidak ada sintaks mirip fungsi Razor yang canggung. Tidak ada tag markup non-standar.

Mengawali jalur dalam atribut HTML apa pun dengan tilde ('~') memberi tahu Razor 2 untuk "membuatnya berfungsi" dengan mengganti jalur yang benar. Itu bagus.

Charles Burns
sumber
Ya, dan mengingat kesederhanaan parsing ~ / prefix, saya bertanya-tanya mengapa sesuatu seperti ini tidak dibangun di ASP.NET sejak awal.
Chris
4
Saya sering menemukan bahwa semakin sederhana desainnya, semakin banyak pemikiran yang masuk ke dalamnya.
Charles Burns
1
Jawaban ini agak menyesatkan. Sintaks yang diposting untuk MVC4 sebenarnya bergantung pada mesin silet. Ini mungkin tidak menggunakan markup khusus, tetapi hanya mesin Razor v2 + yang menangani sintaks yang ditunjukkan dengan benar.
Chris
1
Anda benar, @ Chris. Saya telah memperbarui jawaban untuk mencerminkan ini.
Charles Burns
10

Melanggar perubahan - MVC 5

Hati-hati dengan perubahan perubahan yang merusak di MVC 5 (dari catatan rilis MVC 5 )

Url Rewrite dan Tilde (~)

Setelah memutakhirkan ke ASP.NET Razor 3 atau ASP.NET MVC 5, notasi tilde (~) mungkin tidak lagi berfungsi dengan benar jika Anda menggunakan penulisan ulang URL. URL rewrite mempengaruhi tilde (~) notasi dalam elemen HTML seperti <A/>, <SCRIPT/>,<LINK/> , dan sebagai hasilnya tilde tidak lagi peta ke direktori root.

Misalnya, jika Anda menulis ulang permintaan untuk asp.net/content ke asp.net , atribut href dalam <A href="~/content/"/>menyelesaikannya menjadi / content / content / dan bukan / . Untuk menekan perubahan ini, Anda dapat menyetel konteks IIS_WasUrlRewritten ke false di setiap Halaman Web atau di Application_BeginRequest di Global.asax.

Mereka sebenarnya tidak menjelaskan bagaimana melakukannya, tetapi kemudian saya menemukan jawaban ini :

Jika Anda menjalankan mode IIS 7 Integrated Pipeline, coba letakkan yang berikut ini di Global.asax:

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

Catatan: Anda mungkin ingin memeriksa Request.ServerVariablesbenar-benar berisi IIS_WasUrlRewrittenterlebih dahulu untuk memastikan ini adalah masalah Anda.


PS. Saya pikir saya mengalami situasi di mana hal ini terjadi pada saya dan saya mendapatkan src="~/content/..."URL yang dibuat ke HTML saya - tetapi ternyata ada sesuatu yang tidak menyegarkan ketika kode saya sedang dikompilasi. Mengedit dan menyimpan kembali file Layout dan halaman cshtml entah bagaimana memicu sesuatu untuk bekerja.

Simon_Weaver
sumber
6

Di ASP.NET saya biasanya menggunakan <img src='<%= VirtualPathUtility.ToAbsolute("~/images/logo.gif") %>' alt="Our Company Logo"/>. Saya tidak melihat mengapa solusi serupa tidak berfungsi di ASP.NET MVC.

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

Apa yang saya gunakan. Ubah jalur agar sesuai dengan contoh Anda.

Jesper Palm
sumber
5

Untuk apa nilainya, saya sangat membenci gagasan mengotori aplikasi saya dengan tag server hanya untuk menyelesaikan jalur, jadi saya melakukan sedikit lebih banyak penelitian dan memilih untuk menggunakan sesuatu yang telah saya coba sebelumnya untuk menulis ulang tautan - filter respons. Dengan cara ini, saya dapat mengawali semua jalur absolut dengan awalan yang diketahui dan menggantinya saat runtime menggunakan objek Response.Filter dan tidak perlu khawatir tentang tag server yang tidak perlu. Kode diposting di bawah seandainya itu akan membantu orang lain.

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
sumber
4

Mesin tampilan Razor untuk MVC 3 membuatnya lebih mudah dan lebih bersih untuk menggunakan jalur relatif root-virtual yang diselesaikan dengan benar pada waktu proses. Cukup jatuhkan metode Url.Content () ke dalam nilai atribut href dan itu akan diselesaikan dengan benar.

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

Seperti Chris, saya benar-benar tidak tahan harus meletakkan tag sisi server yang membengkak di dalam markup bersih saya hanya untuk memberi tahu hal bodoh untuk melihat dari akar ke atas. Permintaan itu seharusnya sangat sederhana dan masuk akal. Tetapi saya juga benci gagasan harus berusaha menulis kelas C # khusus untuk melakukan hal yang sesederhana itu, mengapa saya harus melakukannya? Buang-buang waktu.

Bagi saya, saya hanya berkompromi pada "kesempurnaan" dan melakukan hardcode nama jalur akar direktori virtual di dalam referensi jalur saya. Jadi seperti ini:

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

Tidak ada pemrosesan sisi server atau kode C # yang diperlukan untuk menyelesaikan URL, yang terbaik untuk kinerja meskipun saya tahu itu akan dapat diabaikan. Dan tidak ada kekacauan sisi server yang membengkak di markup bersih saya yang bagus.

Saya hanya harus hidup dengan mengetahui bahwa ini adalah hardcode dan perlu dihapus ketika sesuatu bermigrasi ke domain yang tepat, bukan http: // MyDevServer / MyProject /

Bersulang

Aaron
sumber
1
Saya memilih untuk mengembalikan Anda ke 0. Sepenuhnya setuju dengan sentimen Anda. Saya baru mengenal web dev setelah 5 tahun di C # murni dan betapa malangnya tanah spaghetti chaos itu semua.
Luke Puplett
Ini sepertinya kompromi yang dapat diterima sampai Anda perlu melakukan sesuatu seperti menerapkan ke aplikasi web bersarang. Menggunakan mark-up resolver akan memperbaikinya, tetapi tautan statis Anda akan rusak. Contoh: Anda membangun secara lokal dengan server web
bawaan
Ini akan rusak dalam banyak skenario produksi
Oskar Duveborn
Saya sangat menyukai solusi ini: thoughtstuff.co.uk/2013/02/…
Dion
1

Terlambat untuk permainan, tetapi posting ini memiliki ringkasan yang sangat lengkap tentang penanganan jalur ASP.Net.

plyawn
sumber
1

Saya menggunakan metode pembantu sederhana. Anda dapat dengan mudah menggunakannya di Views dan Controllers.

Markup:

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

Metode pembantu:

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