Относительные пути ASP.NET MVC

В моих приложениях мне часто приходится использовать относительные пути. Например, когда я ссылаюсь на 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>

но это не сработает для меня, так как во время цикла разработки мне нужно установить на тестовую машину, на которой приложение будет работать в виртуальном каталоге. Корневые относительные пути не работают, когда корень изменяется. Кроме того, по причинам технического обслуживания я не могу просто изменить все пути на время развертывания теста - это было бы кошмаром само по себе.

Итак, какое лучшее решение?

Edit:

Поскольку этот вопрос все еще принимает мнения и ответы, я подумал, что было бы разумно обновить его, чтобы отметить, что с Razor V2 поддерживается поддержка корневых URL-адресов, поэтому вы можете использовать

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

без синтаксиса на стороне сервера, а механизм просмотра автоматически заменяет ~/любым текущим корнем сайта.

Ответ 1

Попробуйте следующее:

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

Или используйте MvcContrib и выполните следующее:

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

Ответ 2

В то время как старый пост, новые читатели должны знать, что Razor 2 и более поздние версии (по умолчанию в MVC4 +) полностью решают эту проблему.

Старый MVC3 с Razor 1:

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

Новый MVC4 с Razor 2 и более поздними версиями:

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

Отсутствует неудобный синтаксис функции Razor. Нет нестандартных меток разметки.

Префикс пути в любых атрибутах HTML с тильдой ('~') говорит Razor 2 "просто заставить его работать", заменив правильный путь. Это здорово.

Ответ 3

Нарушение изменения - MVC 5

Следите за изменением изменений в MVC 5 (из заметки о выпуске MVC 5)

Переписать Url и Tilde (~)

После перехода на ASP.NET Razor 3 или ASP.NET MVC 5 тильда (~) нотация может перестать работать корректно, если вы используете перезаписи URL. Переписывание URL-адресов влияет на нотацию тильды (~) в HTML-элементах, таких как <A/>, <SCRIPT/>, <LINK/>, и в результате тильда больше не отображает корневой каталог.

Например, если вы переписываете запросы asp.net/content на asp.net, атрибут href в <A href="~/content/"/> разрешается /content/content/ вместо /. Чтобы подавить это изменение, вы можете установить контекст IIS_WasUrlRewritten на false на каждой веб-странице или в 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, чтобы убедиться, что это ваша проблема.


PS. Я думал, что у меня была ситуация, когда это происходило со мной, и я получал src="~/content/..." URLS, сгенерированный в мой HTML-код, но оказалось, что что-то просто не обновлялось при компиляции моего кода. Редактирование и сохранение макетов и файлов cshtml страницы каким-то образом вызвали что-то для работы.

Ответ 4

В ASP.NET я обычно использую <img src='<%= VirtualPathUtility.ToAbsolute("~/images/logo.gif") %>' alt="Our Company Logo"/>. Я не понимаю, почему подобное решение не должно работать в ASP.NET MVC.

Ответ 5

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

Это то, что я использовал. Измените путь в соответствии с вашим примером.

Ответ 6

Для чего это стоит, я действительно ненавижу идею забивать мое приложение тегами сервера только для того, чтобы разрешать пути, поэтому я сделал немного больше исследований и решил использовать то, что я пробовал раньше, для переписывания ссылок - фильтра ответов. Таким образом, я могу префикс всех абсолютных путей с известным префиксом и заменить его во время выполнения с помощью объекта 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);
        }
    }
}

Ответ 7

Механизм просмотра Razor для MVC 3 упрощает и упрощает использование относительных путей виртуального корня, которые должным образом разрешены во время выполнения. Просто отбросьте метод Url.Content() в значение атрибута href и он будет правильно исправляться.

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

Ответ 9

Поздно к игре, но этот пост содержит очень полное резюме обработки путей ASP.Net.

Ответ 10

Я использую простой вспомогательный метод. Вы можете легко использовать его в представлениях и контроллерах.

Разметка:

<a [email protected]()/about">About Us</a>

Вспомогательный метод:

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

Ответ 11

Как и Крис, я действительно не могу терпеть, чтобы поместить разбухшие теги на стороне сервера в мою чистую разметку просто для того, чтобы рассказать глупой вещи, чтобы смотреть от корня вверх. Это должно быть очень простой и разумной задачей. Но я также ненавижу идею приложить усилия, чтобы написать какие-либо пользовательские классы С#, чтобы сделать такую ​​простую вещь, зачем мне это нужно? Какая пустая трата времени.

Для меня я просто скомпрометировал "совершенство" и жестко закодировал имя корневого каталога виртуального каталога внутри ссылок на пути. Так вот так:

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

Нет обработки на стороне сервера или кода С#, необходимого для разрешения URL-адреса, что лучше всего подходит для производительности, хотя я знаю, что это будет незначительно. И без раздутого уродливого хаоса на стороне сервера в моей красивой чистой разметке.

Мне просто нужно жить, зная, что это жестко запрограммировано и нужно будет удалить, когда предмет переносится в соответствующий домен вместо http://MyDevServer/MyProject/

Приветствия