ASP.NET MVC เส้นทางสัมพัทธ์


101

ในแอปพลิเคชันของฉันฉันมักจะต้องใช้เส้นทางสัมพัทธ์ ตัวอย่างเช่นเมื่อฉันอ้างอิง 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 "มันจะถูกส่งตรงไปยังไคลเอนต์ดังนั้นจึงลดอีกสิ่งหนึ่งที่เซิร์ฟเวอร์ต้องทำ? ฉันคิดว่าฉันอ่านที่ไหนยิ่งคุณสามารถหลีกเลี่ยงกระบวนการเซิร์ฟเวอร์ได้มากเท่าไหร่ก็ยิ่งดี - โดยเฉพาะอย่างยิ่งกับเนื้อหาคงที่เช่นเส้นทาง * .js? ฉันรู้ว่าสิ่งนี้ใช้ทรัพยากรน้อยที่สุด แต่ถ้าคุณมี Url.Content () สองสามแสน / พันในแอปของคุณนั่นคือไม่กี่นาโนวินาทีที่ถูกโกนออกใช่ไหม
Losbear

53

ในขณะที่โพสต์เก่าผู้อ่านใหม่ควรทราบว่า Razor 2 และใหม่กว่า (ค่าเริ่มต้นใน MVC4 +) ช่วยแก้ปัญหานี้ได้อย่างสมบูรณ์

MVC3 เก่าพร้อมมีดโกน 1:

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

MVC4 ใหม่พร้อม Razor 2 และใหม่กว่า:

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

ไม่มีไวยากรณ์เหมือนฟังก์ชัน Razor ที่น่าอึดอัดใจ ไม่มีแท็กมาร์กอัปที่ไม่ได้มาตรฐาน

การนำหน้าเส้นทางในแอตทริบิวต์ HTML ใด ๆ ด้วยเครื่องหมายทิลเดอ ('~') จะบอกให้ Razor 2 "ทำให้มันใช้งานได้" โดยแทนที่เส้นทางที่ถูกต้อง มันยอดเยี่ยมมาก


ใช่และด้วยความเรียบง่ายในการแยกวิเคราะห์คำนำหน้า ~ / ฉันสงสัยว่าเหตุใดสิ่งนี้จึงไม่ได้สร้างขึ้นใน ASP.NET ตั้งแต่เริ่มต้น
คริส

4
ฉันมักพบว่ายิ่งดีไซน์เรียบง่ายความคิดก็ยิ่งเข้ามา
Charles Burns

1
คำตอบนี้ทำให้เข้าใจผิดเล็กน้อย ไวยากรณ์ที่โพสต์สำหรับ MVC4 นั้นขึ้นอยู่กับเอนจินมีดโกน อาจไม่ใช้มาร์กอัปพิเศษใด ๆ แต่มีเพียงเอ็นจิ้น Razor v2 + เท่านั้นที่จัดการไวยากรณ์ที่แสดงอย่างถูกต้อง
คริส

1
คุณพูดถูก @Chris ฉันได้อัปเดตคำตอบเพื่อแสดงถึงสิ่งนี้แล้ว
Charles Burns

10

การเปลี่ยนแปลงอย่างสิ้นเชิง - MVC 5

ระวังการเปลี่ยนแปลงอย่างสิ้นเชิงใน MVC 5 (จากบันทึกประจำรุ่น MVC 5 )

Url Rewrite และ Tilde (~)

หลังจากอัปเกรดเป็น ASP.NET Razor 3 หรือ ASP.NET MVC 5 แล้วสัญลักษณ์ tilde (~) อาจทำงานไม่ถูกต้องอีกต่อไปหากคุณใช้การเขียน URL ใหม่ าการเขียน URL ที่ส่งผลกระทบต่อสัญกรณ์ตัวหนอน (~) ในองค์ประกอบ HTML เช่น <A/>, <SCRIPT/>, <LINK/>, และเป็นผลให้ตัวหนอนไม่มีแผนที่ไปยังไดเรกทอรีราก

ตัวอย่างเช่นถ้าคุณเขียนร้องขอสำหรับasp.net/contentเพื่อasp.netแอตทริบิวต์ href ใน <A href="~/content/"/>หายไป / content / เนื้อหา /แทน/ ในการระงับการเปลี่ยนแปลงนี้คุณสามารถตั้งค่าบริบทIIS_WasUrlRewrittenเป็นเท็จในแต่ละเว็บเพจหรือใน Application_BeginRequestใน Global.asax

พวกเขาไม่ได้อธิบายว่าต้องทำอย่างไร แต่แล้วฉันก็พบคำตอบนี้ :

หากคุณกำลังทำงานในโหมด IIS 7 Integrated Pipeline ให้ลองใส่สิ่งต่อไปนี้ในGlobal.asax:

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

หมายเหตุ: คุณอาจต้องตรวจสอบRequest.ServerVariablesว่ามีจริงIIS_WasUrlRewrittenก่อนเพื่อให้แน่ใจว่านี่คือปัญหาของคุณ


ปล. ฉันคิดว่าฉันมีสถานการณ์ที่สิ่งนี้เกิดขึ้นกับฉันและฉันได้รับsrc="~/content/..."URLS ที่สร้างขึ้นใน HTML ของฉัน - แต่กลับกลายเป็นว่ามีบางอย่างไม่สดชื่นเมื่อกำลังคอมไพล์โค้ดของฉัน การแก้ไขและบันทึกไฟล์ Layout และ page cshtml ใหม่ทำให้บางอย่างทำงาน


6

ใน ASP.NET ฉันมักจะใช้<img src='<%= VirtualPathUtility.ToAbsolute("~/images/logo.gif") %>' alt="Our Company Logo"/>. ฉันไม่เห็นว่าทำไมโซลูชันที่คล้ายกันไม่ควรทำงานใน ASP.NET MVC



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

เอ็นจิ้นมุมมอง Razor สำหรับ MVC 3 ช่วยให้ใช้พา ธ สัมพัทธ์รูทเสมือนได้ง่ายและสะอาดยิ่งขึ้นซึ่งได้รับการแก้ไขอย่างเหมาะสมในขณะทำงาน เพียงแค่วางเมธอด Url.Content () ลงในค่าแอตทริบิวต์ href และจะแก้ไขได้อย่างถูกต้อง

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

1

เช่นเดียวกับคริสฉันทนไม่ได้จริงๆที่ต้องใส่แท็กฝั่งเซิร์ฟเวอร์ที่ป่องไว้ในมาร์กอัปที่สะอาดของฉันเพียงเพื่อบอกสิ่งที่โง่เขลาที่มองจากรากขึ้นไป นั่นควรเป็นสิ่งที่ง่ายและสมเหตุสมผลที่จะขอ แต่ฉันก็เกลียดความคิดที่จะต้องพยายามเขียนคลาส C # ที่กำหนดเองเพื่อทำสิ่งง่ายๆทำไมฉันต้องทำด้วย? ช่างเป็นอะไรที่เสียเวลา

สำหรับฉันฉันเพียงแค่บุกรุก "ความสมบูรณ์แบบ" และฮาร์ดโค้ดชื่อพา ธ ของไดเรกทอรีเสมือนภายในการอ้างอิงพา ธ ของฉัน เป็นเช่นนี้:

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

ไม่ต้องใช้การประมวลผลฝั่งเซิร์ฟเวอร์หรือรหัส C # ในการแก้ไข URL ซึ่งดีที่สุดสำหรับประสิทธิภาพแม้ว่าฉันจะรู้ว่ามันจะเล็กน้อยก็ตาม และไม่มีความวุ่นวายฝั่งเซิร์ฟเวอร์ที่น่าเกลียดป่องในมาร์กอัปที่สะอาดดีของฉัน

ฉันจะต้องอยู่กับที่รู้ว่านี่เป็นฮาร์ดโค้ดและจะต้องถูกลบออกเมื่อสิ่งนั้นย้ายไปยังโดเมนที่เหมาะสมแทนที่จะเป็นhttp: // MyDevServer / MyProject /

ไชโย


1
ฉันโหวตให้คุณกลับมาที่ 0 เห็นด้วยกับความรู้สึกของคุณโดยสิ้นเชิง ฉันยังใหม่กับนักพัฒนาเว็บหลังจาก 5 ปีใน C # ที่บริสุทธิ์และมันเป็นดินแดนแห่งความโกลาหลของสปาเก็ตตี้ที่หายนะ
Luke Puplett

ดูเหมือนว่าเป็นการประนีประนอมที่ยอมรับได้จนกว่าคุณจะต้องทำบางอย่างเช่นปรับใช้กับเว็บแอปที่ซ้อนกัน การใช้มาร์กอัปตัวแก้ไขจะแก้ไขปัญหานี้ได้ แต่ลิงก์แบบคงที่ของคุณจะใช้งานไม่ได้ ตัวอย่าง: คุณสร้างในเครื่องกับเว็บเซิร์ฟเวอร์ในตัวจากนั้นดันแอปไปที่ domain.com/myNewWebApp
วางจำหน่าย

สิ่งนี้จะทำลายสถานการณ์การผลิตจำนวนมาก
Oskar Duveborn

ฉันชอบวิธีนี้มาก: thoughtstuff.co.uk/2013/02/…
Dion


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.