简体   繁体   English

ASP.NET MVC相对路径

[英]ASP.NET MVC Relative Paths

In my applications, I often have to use relative paths. 在我的应用程序中,我经常需要使用相对路径。 For example, when I reference JQuery, I usually do so like this: 例如,当我引用JQuery时,我通常这样做:

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

Now that I'm making the transition to MVC, I need to account for the different paths a page might have, relative to the root. 现在我正在转换到MVC,我需要考虑页面可能具有的相对于根的不同路径。 This was of course an issue with URL rewriting in the past, but I managed to work around it by using consistent paths. 这当然是过去URL重写的问题,但我设法通过使用一致的路径来解决它。

I'm aware that the standard solution is to use absolute paths such as: 我知道标准解决方案是使用绝对路径,例如:

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

but this will not work for me as during the development cycle, I have to deploy to a test machine on which the app will run in a virtual directory. 但这对我来说不起作用,因为在开发周期中,我必须部署到应用程序将在虚拟目录中运行的测试机器上。 Root relative paths don't work when the root changes. 根更改时,根相对路径不起作用。 Also, for maintenance reasons, I cannot simply change out all the paths for the duration of deploying the test - that would be a nightmare in itself. 此外,出于维护原因,我不能简单地在部署测试期间更改所有路径 - 这本身就是一场噩梦。

So what's the best solution? 那么什么是最好的解决方案?

Edit: 编辑:

Since this question is still receiving views and answers, I thought it might be prudent to update it to note that as of Razor V2, support for root-relative urls is baked in, so you can use 由于这个问题仍在接受观点和答案,我认为更新它可能是谨慎的,因为从Razor V2开始,支持根相对网址,所以你可以使用

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

without any server-side syntax, and the view engine automatically replaces ~/ with whatever the current site root is. 没有任何服务器端语法,视图引擎自动替换〜/当前站点根目录。

Try this: 试试这个:

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

Or use MvcContrib and do this: 或者使用MvcContrib并执行以下操作:

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

While an old post, new readers should know that Razor 2 and later (default in MVC4+) completely resolves this problem. 虽然是一篇旧帖,新读者应该知道Razor 2及更高版本(MVC4 +中的默认值)完全解决了这个问题。

Old MVC3 with Razor 1: 使用Razor 1的旧MVC3:

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

New MVC4 with Razor 2 and later: 使用Razor 2和更高版本的新MVC4:

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

No awkward Razor function-like syntax. 没有笨拙的Razor函数式语法。 No non-standard markup tags. 没有非标准的标记标记。

Prefixing a path in any HTML attributes with a tilde ('~') tells Razor 2 to "just make it work" by substituting the correct path. 使用波形符('〜')在任何HTML属性中添加路径,通过替换正确的路径告诉Razor 2“只使其工作”。 It's great. 这很棒。

Breaking change - MVC 5 突破性的变化 - MVC 5

Watch out for a breaking change change in MVC 5 (from the MVC 5 release notes ) 注意MVC 5中的重大变化(来自MVC 5发行说明

Url Rewrite and Tilde(~) Url Rewrite和Tilde(〜)

After upgrading to ASP.NET Razor 3 or ASP.NET MVC 5, the tilde(~) notation may no longer work correctly if you are using URL rewrites. 升级到ASP.NET Razor 3或ASP.NET MVC 5后,如果使用URL重写,则波形符(〜)表示法可能无法正常工作。 The URL rewrite affects the tilde(~) notation in HTML elements such as <A/> , <SCRIPT/> , <LINK/> , and as a result the tilde no longer maps to the root directory. URL重写会影响HTML元素中的波浪号(〜)表示法,例如<A/><SCRIPT/><LINK/> ,因此波形符号不再映射到根目录。

For example, if you rewrite requests for asp.net/content to asp.net , the href attribute in <A href="~/content/"/> resolves to /content/content/ instead of / . 例如,如果您将asp.net/content的请求重写为asp.net ,则<A href="~/content/"/>的href属性将解析为/ content / content /而不是/ To suppress this change, you can set the IIS_WasUrlRewritten context to false in each Web Page or in Application_BeginRequest in Global.asax. 要禁止此更改,可以在每个Web页面或Global.asax中的Application_BeginRequest中将IIS_WasUrlRewritten上下文设置为false。

They don't actually explain how to do it, but then I found this answer : 他们实际上没有解释如何做到这一点,但后来我找到了这个答案

If you are running in IIS 7 Integrated Pipeline mode try putting the following in your Global.asax : 如果您在IIS 7集成管道模式下运行,请尝试将以下内容放入Global.asax

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

Note: You may want to check Request.ServerVariables actually contains IIS_WasUrlRewritten first to be sure this is what your problem is. 注意:您可能要检查Request.ServerVariables实际上是否包含IIS_WasUrlRewritten ,以确保这是您的问题所在。


PS. PS。 I thought I had a situation where this was happening to me and I was getting src="~/content/..." URLS generated into my HTML - but it turned out something just wasn't refreshing when my code was being compiled. 我以为我遇到了这种情况发生在我身上并且我得到了src="~/content/..."我的HTML中生成了一个URL - 但事实证明,当我的代码编译时,一些东西并不令人耳目一新。 Editing and resaving the Layout and page cshtml files somehow triggered something to work. 编辑和重新保存布局和页面cshtml文件以某种方式触发了一些工作。

In ASP.NET I usually use <img src='<%= VirtualPathUtility.ToAbsolute("~/images/logo.gif") %>' alt="Our Company Logo"/> . 在ASP.NET中,我通常使用<img src='<%= VirtualPathUtility.ToAbsolute("~/images/logo.gif") %>' alt="Our Company Logo"/> I don't see why a similar solution shouldn't work in ASP.NET MVC. 我不明白为什么类似的解决方案不适用于ASP.NET MVC。

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

Is what I used. 是我用过的。 Change path to match your example. 更改路径以匹配您的示例。

For what it's worth, I really hate the idea of littering my app with server tags just to resolve paths, so I did a bit more research and opted to use something I'd tried before for rewriting links - a response filter. 对于它的价值,我真的很讨厌用服务器标签乱扔我的应用程序只是为了解决路径,所以我做了一些研究,并选择使用我之前尝试过的重写链接 - 响应过滤器。 In this way, I can prefix all absolute paths with a known prefix and replace it at runtime using the Response.Filter object and not have to worry about unnecessary server tags. 通过这种方式,我可以使用已知前缀为所有绝对路径添加前缀,并在运行时使用Response.Filter对象替换它,而不必担心不必要的服务器标记。 The code is posted below in case it will help anyone else. 代码发布在下面,以防它可以帮助其他任何人。

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);
        }
    }
}

The Razor view engine for MVC 3 makes it even easier and cleaner to use virtual-root relative paths that are properly resolved at run-time. MVC 3的Razor视图引擎使得使用在运行时正确解析的虚拟根相对路径变得更加容易和清晰。 Just drop the Url.Content() method into the href attribute value and it will resolve properly. 只需将Url.Content()方法放入href属性值即可正确解析。

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

Like Chris, I really can't stand having to put bloated server-side tags inside my clean markup just purely to tell the stupid thing to look from the root upwards. 像Chris一样,我真的不能忍受在我干净的标记中放置膨胀的服务器端标签,纯粹是为了告诉愚蠢的东西从根向上看。 That should be a very simple, reasonable thing to ask for. 这应该是一个非常简单,合理的要求。 But I also hate the idea of having to go to the effort of writing any custom C# classes to do such a simple thing, why should I have to? 但我也讨厌不得不努力编写任何自定义C#类来做这么简单的事情,为什么我要这么做? What a waste of time. 真是浪费时间。

For me, I simply compromised on "perfection" and hardcoded the virtual directory's root path name inside my path references. 对我来说,我只是在“完美”上妥协,并在我的路径引用中硬编码虚拟目录的根路径名。 So like this: 像这样:

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

No server-side processing or C# code required to resolve the URL, which is best for performance although I know it would be negligible regardless. 解析URL不需要服务器端处理或C#代码,这对性能最好,尽管我知道无论如何都可以忽略不计。 And no bloated ugly server-side chaos in my nice clean markup. 在我干净的标记中没有臃肿丑陋的服务器端混乱。

I'll just have to live with knowing that this is hardcoded and will need to be removed when the thing migrates to a proper domain instead of http://MyDevServer/MyProject/ 我只需要知道这是硬编码的,并且当事物迁移到适当的域而不是http:// MyDevServer / MyProject /时需要删除

Cheers 干杯

I went with a bit of a different approach based off a similar SO post, but with much less code... 基于类似的SO帖子,我采用了一种不同的方法,但代码少得多......

http://a.shinynew.me/post/6042784654/relative-paths-in-asp-net-mvc-javascript http://a.shinynew.me/post/6042784654/relative-paths-in-asp-net-mvc-javascript

游戏后期,但这篇文章对处理ASP.Net路径有一个非常完整的总结。

I use a simple helper method. 我使用一个简单的帮助方法。 You can easily use it in the Views and Controllers. 您可以在视图和控制器中轻松使用它。

Markup: 标记:

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

Helper method: 辅助方法:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM