ASP.NET MVC Göreli Yolları


100

Uygulamalarımda genellikle göreceli yollar kullanmak zorunda kalıyorum. Örneğin, JQuery'ye başvurduğumda, genellikle şu şekilde yaparım:

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

Artık MVC'ye geçiş yaptığıma göre, bir sayfanın sahip olabileceği farklı yolları köke göre hesaba katmam gerekiyor. Bu elbette geçmişte URL yeniden yazma ile ilgili bir sorundu, ancak tutarlı yollar kullanarak bu sorunu çözmeyi başardım.

Standart çözümün aşağıdakiler gibi mutlak yollar kullanmak olduğunun farkındayım:

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

ancak bu, geliştirme döngüsü sırasında benim için çalışmayacak, uygulamanın sanal bir dizinde çalışacağı bir test makinesine dağıtmam gerekiyor. Kök değiştiğinde kök göreceli yollar çalışmaz. Ayrıca, bakım nedenlerinden ötürü, testi uygulama süresi boyunca tüm yolları basitçe değiştiremem - bu başlı başına bir kabus olur.

Peki en iyi çözüm nedir?

Düzenle:

Bu soru hala görüş ve cevaplar aldığından, Razor V2'den itibaren köke bağlı URL'lerin desteğinin pişirildiğini, böylece kullanabilmeniz için güncellemenin akıllıca olacağını düşündüm.

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

herhangi bir sunucu tarafı sözdizimi olmadan ve görünüm motoru otomatik olarak ~ / yerine geçerli site kökü ne ise onu değiştirir.

Yanıtlar:


93

Bunu dene:

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

Veya MvcContrib'i kullanın ve şunu yapın:

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

1
Bu çok sık soruluyor, bir SSS olmalı, bence şablona bir örnek eklemeleri gerekiyor.
Simon Steele

Harika, bu beni gerçekten zor durumda bıraktı. Teşekkürler!
Jared

2
(Bu gönderinin eski olduğunu biliyorum) - <% = Url.Content ("~ / Scripts / jquery-1.2.6.js")%> kullanılmaması, sunucunun yolu oluşturmasına neden olurken, "/ Scripts / jquery-1.2.6.js ", doğrudan istemciye sunulacak, dolayısıyla sunucunun yapması gereken bir şeyi daha mı azaltıyorsunuz? Bir yerde okuduğumu düşündüm, sunucu sürecinden ne kadar kaçınırsanız, o kadar iyi - özellikle * .js yolları gibi statik içerikle? Bunun minimum kaynak kullandığını biliyorum, ancak uygulamanızda birkaç yüz / bin Url.Content () varsa, bu birkaç nanosaniye kesildi, değil mi?
Losbear

53

Eski bir gönderi olsa da, yeni okuyucular Razor 2 ve sonraki sürümlerin (MVC4 + 'da varsayılan) bu sorunu tamamen çözdüğünü bilmelidir.

Razor 1 ile eski MVC3:

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

Razor 2 ve sonraki sürümlerle yeni MVC4:

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

Tuhaf Razor işlevi benzeri sözdizimi yok. Standart olmayan işaretleme etiketi yok.

Herhangi bir HTML özniteliğindeki bir yolun önüne yaklaşık işareti ('~') koymak, Razor 2'ye doğru yolu değiştirerek "çalışmasını sağlamasını" söyler. Bu harika.


Evet, ve ~ / öneki ayrıştırmanın basitliği göz önüne alındığında, neden böyle bir şeyin baştan ASP.NET'te yerleşik olmadığını merak ediyorum.
Chris

4
Çoğu zaman, tasarım ne kadar basitse, o kadar çok düşündüğünü fark ettim.
Charles Burns

1
Bu cevap biraz yanıltıcıdır. MVC4 için gönderilen sözdizimi aslında jilet motoruna bağlıdır. Herhangi bir özel işaret kullanmayabilir, ancak yalnızca Razor v2 + motoru doğru şekilde gösterilen sözdizimini işler.
Chris

1
Haklısın @Chris. Cevabı bunu yansıtacak şekilde güncelledim.
Charles Burns

10

Son derece değişim - MVC 5

MVC 5'teki önemli değişikliğe dikkat edin ( MVC 5 sürüm notlarından )

Url Yeniden Yazma ve Tilde (~)

ASP.NET Razor 3 veya ASP.NET MVC 5'e yükselttikten sonra, URL yeniden yazmaları kullanıyorsanız tilde (~) gösterimi artık düzgün çalışmayabilir. URL yeniden yazma gibi HTML öğeleri içinde tilde (~) gösterimini etkiler <A/>, <SCRIPT/>, <LINK/>, ve sonuç olarak tilde artık kök dizinine eşler.

Örneğin, istekleri yeniden eğer asp.net/content için asp.net içinde href özelliği <A href="~/content/"/>ile giderir / içerik / içerik / yerine / ' . Bu değişikliği bastırmak için, IIS_WasUrlRewritten bağlamını her Web Sayfasında veya Global.asax'taki Application_BeginRequest'te false olarak .

Aslında nasıl yapılacağını açıklamıyorlar ama sonra şu cevabı buldum :

IIS 7 Integrated Pipeline modunda çalıştırıyorsanız, aşağıdakileri uygulamanıza koymayı deneyin Global.asax:

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

Not: kontrol etmek isteyebilir Request.ServerVariablesaslında içeren IIS_WasUrlRewrittenemin bu sorunun ne olduğunu olmayı ilk.


PS. Bunun başıma geldiği bir durum olduğunu sanıyordum ve src="~/content/..."URL’lerin HTML’imde oluşturulmasını sağlıyordum - ancak kodum derlenirken bir şeylerin yenilenemediği ortaya çıktı. Düzen ve sayfa cshtml dosyalarının düzenlenmesi ve yeniden kaydedilmesi bir şekilde çalışması için bir şeyi tetikledi.


6

ASP.NET'te genellikle kullanırım <img src='<%= VirtualPathUtility.ToAbsolute("~/images/logo.gif") %>' alt="Our Company Logo"/> . Benzer bir çözümün neden ASP.NET MVC'de çalışmaması gerektiğini anlamıyorum.


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

Kullandığım şey. Örneğinizle eşleşecek yolu değiştirin.


5

Ne olursa olsun, sadece yolları çözmek için uygulamamı sunucu etiketleriyle doldurma fikrinden gerçekten nefret ediyorum, bu yüzden biraz daha araştırma yaptım ve bağlantıları yeniden yazmak için daha önce denediğim bir şeyi kullanmayı tercih ettim - bir yanıt filtresi. Bu şekilde, tüm mutlak yolların önüne bilinen bir önek ekleyebilir ve bunu, Response.Filter nesnesini kullanarak çalışma zamanında değiştirebilirim ve gereksiz sunucu etiketleri konusunda endişelenmeme gerek kalmaz. Başkalarına yardımcı olması ihtimaline karşı kod aşağıda yayınlanmıştır.

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 için Razor görünüm altyapısı, çalışma zamanında düzgün şekilde çözümlenen sanal köke göreli yolları kullanmayı daha da kolay ve daha temiz hale getirir. Url.Content () yöntemini href öznitelik değerine bırakmanız yeterlidir, bu doğru şekilde çözülecektir.

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

1

Chris gibi, sadece aptal şeye kökten yukarı bakmasını söylemek için temiz işaretlememin içine şişirilmiş sunucu tarafı etiketleri koymaya gerçekten dayanamıyorum. Bunu istemek çok basit, makul bir şey olmalı. Ama aynı zamanda bu kadar basit bir şey yapmak için herhangi bir özel C # sınıfı yazma çabasına gitme fikrinden de nefret ediyorum, neden bunu yapmalıyım? Ne kadar zaman kaybı.

Benim için, "mükemmellikten" ödün verdim ve sanal dizinin kök yol adını yol referanslarımın içine gömdüm. Bunun gibi:

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

URL'yi çözmek için hiçbir sunucu tarafı işleme veya C # kodu gerekmez, bu performans için en iyisidir, ancak bunun göz ardı edilebilir olacağını biliyorum. Ve güzel temiz işaretlememde şişirilmiş, çirkin sunucu tarafı kaosu yok.

Sadece bunun kodlanmış olduğunu ve şey http: // MyDevServer / MyProject / yerine uygun bir etki alanına taşındığında kaldırılması gerekeceğini bilerek yaşamak zorundayım.

Şerefe


1
Seni 0'a döndürmek için oy verdim. Duygularınıza tamamen katılıyorum. Saf C #'da 5 yıl geçirdikten sonra web geliştiricisinde yeniyim ve ne kadar felaket bir spagetti kaosu ülkesi hepsi bu.
Luke Puplett

İç içe geçmiş bir web uygulamasına dağıtmak gibi bir şey yapmanız gerekene kadar bu kabul edilebilir bir uzlaşma gibi görünüyor. Çözümleyici işaretlemesini kullanmak bunu düzeltir ancak statik bağlantınız kopar. Örnek: yerleşik web sunucusuna karşı yerel olarak
derler

Bu, birçok üretim senaryosunu
kıracak

Bu çözümü oldukça beğendim: thinkstuff.co.uk/2013/02/…
Dion


1

Basit bir yardımcı yöntem kullanıyorum. Görünümler ve Denetleyicilerde kolayca kullanabilirsiniz.

İşaretleme:

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

Yardımcı yöntem:

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

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.