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">

无需任何服务器端语法,并且视图引擎会自动将〜/替换为当前站点根目录。

Answers:


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
这个问题经常被问到,应该是一个常见问题解答,我认为他们需要在模板中包含一个示例。
西蒙·斯蒂尔

太棒了,这真的使我无所适从。谢谢!
杰瑞德(Jared)'2009年

2
(我知道这篇文章很旧)-不使用<%= Url.Content(“〜/ Scripts / jquery-1.2.6.js”)%>使服务器呈现路径,但是,如果您使用“ / Scripts / jquery-1.2.6.js”,它将直接提供给客户端,因此,减少了服务器必须做的一件事?我以为我读的内容越多,可以避免使用服务器进程越好-尤其是对于* .js路径等静态内容?我意识到这使用了最少的资源,但是如果您的应用程序中有成百上千的Url.Content(),那将节省几纳秒,不是吗?
Losbear 2012年

53

虽然是旧文章,但新读者应该知道Razor 2和更高版本(MVC4 +中的默认值)可以完全解决此问题。

带有剃刀的旧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
我经常发现,设计越简单,考虑的内容就越多。
查尔斯·伯恩斯

1
这个答案有点误导。实际上,为MVC4发布的语法取决于剃刀引擎。它可能不使用任何特殊标记,但只有Razor v2 +引擎可以正确处理语法。
克里斯,

1
你说得对,@克里斯。我已经更新了答案以反映这一点。
查尔斯·伯恩斯

10

重大变化-MVC 5

注意MVC 5中的重大更改(来自MVC 5发行说明

网址重写和字幕(〜)

升级到ASP.NET Razor 3或ASP.NET MVC 5后,如果使用URL重写,则波浪号(〜)表示法可能不再正确。的URL重写影响HTML元素,如波浪线(〜)表示法 <A/><SCRIPT/><LINK/>,并且作为结果的波形符不再映射到根目录。

例如,如果你重写的请求asp.net/contentasp.net,href属性 <A href="~/content/"/>解析为 /内容/内容/代替/。若要取消此更改,可以在每个网页或Global.asax的Application_BeginRequest中将IIS_WasUrlRewrite上下文设置为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中生成URLS-但是事实证明,编译我的代码时并没有刷新。编辑和重新保存Layout和页面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

用于MVC 3的Razor视图引擎使使用虚拟根相对路径(在运行时已正确解析)变得更加轻松和整洁。只需将Url.Content()方法放到href属性值中,它将正确解析。

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

1

像克里斯一样,我真的不能忍受只将clean肿的服务器端标签放在干净的标记中只是为了告诉愚蠢的东西从根本上看。这应该是一件非常简单,合理的事情。但是我也讨厌这样的想法:必须努力编写任何自定义C#类来做这样简单的事情,为什么我必须这样做?真浪费时间。

对我来说,我只是妥协了“完美”,并在路径引用中将虚拟目录的根路径名称硬编码。像这样:

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

无需服务器端处理或C#代码即可解析该URL,尽管我知道无论如何它都可以忽略不计,但这是提高性能的最佳方法。在我的干净标记中,没有膨胀的服务器端丑陋混乱。

我只需要知道这是经过硬编码的,并且在将其迁移到正确的域而不是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";
    }
}

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.