Đường dẫn tương đối ASP.NET MVC


100

Trong các ứng dụng của tôi, tôi thường phải sử dụng các đường dẫn tương đối. Ví dụ: khi tôi tham chiếu JQuery, tôi thường làm như thế này:

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

Bây giờ tôi đang thực hiện chuyển đổi sang MVC, tôi cần tính đến các đường dẫn khác nhau mà một trang có thể có, liên quan đến thư mục gốc. Tất nhiên đây là một vấn đề với việc viết lại URL trong quá khứ, nhưng tôi đã xoay sở để khắc phục bằng cách sử dụng các đường dẫn nhất quán.

Tôi biết rằng giải pháp tiêu chuẩn là sử dụng các đường dẫn tuyệt đối như:

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

nhưng điều này sẽ không hiệu quả với tôi vì trong chu kỳ phát triển, tôi phải triển khai đến một máy thử nghiệm mà ứng dụng sẽ chạy trong một thư mục ảo. Các đường dẫn tương đối gốc không hoạt động khi gốc thay đổi. Ngoài ra, vì lý do bảo trì, tôi không thể đơn giản thay đổi tất cả các đường dẫn trong suốt thời gian triển khai thử nghiệm - bản thân đó sẽ là một cơn ác mộng.

Vậy giải pháp tốt nhất là gì?

Biên tập:

Vì câu hỏi này vẫn đang nhận được lượt xem và câu trả lời, tôi nghĩ có thể cần thận trọng khi cập nhật nó để lưu ý rằng kể từ Razor V2, hỗ trợ cho các url tương đối gốc được tích hợp, vì vậy bạn có thể sử dụng

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

mà không có bất kỳ cú pháp phía máy chủ nào và công cụ chế độ xem tự động thay thế ~ / bằng bất kỳ gốc trang web hiện tại nào.

Câu trả lời:


93

Thử cái này:

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

Hoặc sử dụng MvcContrib và thực hiện việc này:

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

1
Điều này được hỏi thường xuyên nên nó phải là một Câu hỏi thường gặp, tôi nghĩ rằng họ cần bao gồm một ví dụ trong mẫu.
Simon Steele

Tuyệt vời, điều này thực sự giúp tôi thoát khỏi ràng buộc. Cảm ơn!
Jared

2
(Tôi biết bài đăng này đã cũ) - Không sử dụng <% = Url.Content ("~ / Scripts / jquery-1.2.6.js")%> khiến máy chủ hiển thị đường dẫn, ngược lại, nếu bạn đã sử dụng "/ Scripts / jquery-1.2.6.js ", nó sẽ chỉ được cung cấp trực tiếp đến máy khách, do đó, giảm bớt một việc mà máy chủ phải làm? Tôi nghĩ rằng tôi đã đọc ở đâu đó thì bạn càng có thể tránh phải xử lý máy chủ, càng tốt - đặc biệt là với nội dung tĩnh như đường dẫn * .js? Tôi nhận ra rằng điều này sử dụng tài nguyên tối thiểu, nhưng nếu bạn có vài trăm / nghìn Url.Content () trong ứng dụng của mình, đó là một vài nano giây bị cắt bỏ, phải không?
Losbear

52

Trong khi một bài đăng cũ, độc giả mới nên biết rằng Razor 2 trở lên (mặc định trong MVC4 +) giải quyết hoàn toàn vấn đề này.

MVC3 cũ với Razor 1:

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

MVC4 mới với Razor 2 trở lên:

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

Không có cú pháp giống hàm Razor khó xử. Không có thẻ đánh dấu không chuẩn.

Việc bắt đầu một đường dẫn trong bất kỳ thuộc tính HTML nào bằng dấu ngã ('~') cho Razor 2 biết "chỉ làm cho nó hoạt động" bằng cách thay thế đường dẫn chính xác. Thật tuyệt vời.


Có, và với sự đơn giản của việc phân tích tiền tố ~ /, tôi tự hỏi tại sao một cái gì đó như thế này không được tích hợp vào ASP.NET ngay từ đầu.
Chris

4
Tôi thường thấy rằng thiết kế càng đơn giản thì càng có nhiều suy nghĩ về nó.
Charles Burns,

1
Câu trả lời này hơi sai lầm. Cú pháp được đăng cho MVC4 trên thực tế phụ thuộc vào công cụ dao cạo. Nó có thể không sử dụng bất kỳ đánh dấu đặc biệt nào, nhưng chỉ công cụ Razor v2 + xử lý cú pháp được hiển thị chính xác.
Chris

1
Bạn nói đúng, @Chris. Tôi đã cập nhật câu trả lời để phản ánh điều này.
Charles Burns

10

Thay đổi đột phá - MVC 5

Hãy chú ý đến sự thay đổi đột phá trong MVC 5 (từ ghi chú phát hành MVC 5 )

Viết lại url và dấu gạch ngang (~)

Sau khi nâng cấp lên ASP.NET Razor 3 hoặc ASP.NET MVC 5, ký hiệu dấu ngã (~) có thể không còn hoạt động chính xác nếu bạn đang sử dụng tính năng ghi lại URL. Các URL viết lại ảnh hưởng đến các ký hiệu dấu ngã (~) trong các phần tử HTML như <A/>, <SCRIPT/>, <LINK/>, và kết quả là các dấu ngã không còn ánh xạ vào thư mục gốc.

Ví dụ: nếu bạn viết lại các yêu cầu cho asp.net/content thành asp.net , thuộc tính href trong <A href="~/content/"/>giải quyết thành / content / content / thay vì / . Để ngăn chặn thay đổi này, bạn có thể đặt ngữ cảnh IIS_WasUrlRewritten thành false trong mỗi Trang Web hoặc trong Application_BeginRequest trong Global.asax.

Họ không thực sự giải thích cách làm điều đó, nhưng sau đó tôi tìm thấy câu trả lời sau:

Nếu bạn đang chạy ở chế độ Đường ống tích hợp IIS 7, hãy thử đưa những điều sau vào Global.asax:

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

Lưu ý: Bạn có thể muốn kiểm tra Request.ServerVariablesthực sự chứa IIS_WasUrlRewrittentrước để chắc chắn rằng đây là vấn đề của bạn.


Tái bút. Tôi nghĩ rằng tôi đã gặp phải tình huống mà điều này đang xảy ra với tôi và tôi đang nhận được src="~/content/..."URL được tạo vào HTML của mình - nhưng hóa ra có điều gì đó không được làm mới khi mã của tôi đang được biên dịch. Việc chỉnh sửa và lưu lại các tệp cshtml Bố cục và trang bằng cách nào đó đã kích hoạt một thứ gì đó hoạt động.


6

Trong ASP.NET tôi thường sử dụng <img src='<%= VirtualPathUtility.ToAbsolute("~/images/logo.gif") %>' alt="Our Company Logo"/>. Tôi không hiểu tại sao một giải pháp tương tự không hoạt động trong ASP.NET MVC.


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

Là những gì tôi đã sử dụng. Thay đổi đường dẫn để phù hợp với ví dụ của bạn.


5

Đối với những gì nó đáng giá, tôi thực sự ghét ý tưởng rải rác ứng dụng của mình với các thẻ máy chủ chỉ để giải quyết các đường dẫn, vì vậy tôi đã nghiên cứu thêm một chút và chọn sử dụng một thứ mà tôi đã thử trước đây để viết lại các liên kết - một bộ lọc phản hồi. Bằng cách này, tôi có thể đặt tiền tố cho tất cả các đường dẫn tuyệt đối bằng một tiền tố đã biết và thay thế nó trong thời gian chạy bằng đối tượng Response.Filter và không phải lo lắng về các thẻ máy chủ không cần thiết. Mã được đăng dưới đây trong trường hợp nó sẽ giúp ích cho bất kỳ ai khác.

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

Công cụ chế độ xem Razor cho MVC 3 giúp việc sử dụng các đường dẫn tương đối gốc ảo được giải quyết đúng cách tại thời điểm chạy thậm chí còn dễ dàng và sạch sẽ hơn. Chỉ cần thả phương thức Url.Content () vào giá trị thuộc tính href và nó sẽ phân giải đúng.

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

1

Giống như Chris, tôi thực sự không thể chịu đựng được việc phải đặt các thẻ phía máy chủ cồng kềnh bên trong đánh dấu sạch của mình chỉ đơn thuần là để nói điều ngu ngốc là nhìn từ gốc trở lên. Đó phải là một điều rất đơn giản, hợp lý để yêu cầu. Nhưng tôi cũng ghét ý tưởng phải nỗ lực viết bất kỳ lớp C # tùy chỉnh nào để làm một việc đơn giản như vậy, tại sao tôi phải làm vậy? Thật là lãng phí thời gian.

Đối với tôi, tôi chỉ đơn giản là thỏa hiệp về "sự hoàn hảo" và mã hóa cứng tên đường dẫn gốc của thư mục ảo bên trong các tham chiếu đường dẫn của tôi. Vì vậy, như thế này:

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

Không cần xử lý phía máy chủ hoặc mã C # để phân giải URL, điều này là tốt nhất cho hiệu suất mặc dù tôi biết nó sẽ không đáng kể bất kể. Và không có sự hỗn loạn phía máy chủ xấu xí nào trong đánh dấu sạch đẹp của tôi.

Tôi sẽ phải sống chung với việc biết rằng điều này đã được mã hóa cứng và sẽ cần được xóa khi thứ đó di chuyển sang một miền thích hợp thay vì http: // MyDevServer / MyProject /

Chúc mừng


1
Tôi đã bỏ phiếu để đưa bạn trở lại con số 0. Hoàn toàn đồng ý với ý kiến ​​của bạn. Tôi mới làm quen với nhà phát triển web sau 5 năm sử dụng C # thuần túy và tất cả là một vùng đất đầy hỗn loạn của mì spaghetti.
Luke Puplett,

Đây có vẻ như là một thỏa hiệp có thể chấp nhận được cho đến khi bạn cần thực hiện một số việc như triển khai ứng dụng web lồng nhau. Sử dụng đánh dấu trình phân giải sẽ khắc phục điều này nhưng liên kết tĩnh của bạn sẽ bị hỏng. Ví dụ: bạn xây dựng tại địa phương chống lại được xây dựng trong máy chủ web và sau đó đẩy ứng dụng domain.com/myNewWebApp
plyawn

Điều này sẽ phá vỡ trong rất nhiều kịch bản sản xuất
Oskar Duveborn

Tôi khá thích giải pháp này: thinkstuff.co.uk/2013/02/…
Dion

1

Đến muộn với trò chơi, nhưng bài đăng này có một bản tóm tắt rất đầy đủ về việc xử lý các đường dẫn ASP.Net.


1

Tôi sử dụng một phương pháp trợ giúp đơn giản. Bạn có thể dễ dàng sử dụng nó trong Chế độ xem và Bộ điều khiển.

Đánh dấu:

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

Phương pháp người trợ giúp:

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

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.