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

서버 측 구문없이 뷰 엔진은 자동으로 ~ /를 현재 사이트 루트로 대체합니다.

답변:


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 +의 기본값)이이 문제를 완전히 해결한다는 것을 알아야합니다.

Razor 1을 사용하는 이전 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
나는 종종 디자인이 단순할수록 더 많은 생각이 들어간다는 것을 발견했습니다.
Charles Burns

1
이 대답은 약간 오해의 소지가 있습니다. MVC4에 대해 게시 된 구문은 실제로 면도기 엔진에 따라 다릅니다. 특수 마크 업을 사용하지 않을 수 있지만 Razor v2 + 엔진 만 올바르게 표시된 구문을 처리합니다.
크리스

1
맞아요, @Chris. 이것을 반영하기 위해 답변을 업데이트했습니다.
Charles Burns

10

브레이킹 체인지-MVC 5

MVC 5의 주요 변경 사항을 확인하십시오 ( MVC 5 릴리스 정보에서 ).

URL 재 작성 및 물결표 (~)

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_WasUrlRewritten 컨텍스트를 false로 설정할 수 있습니다 .

실제로 수행하는 방법을 설명하지는 않지만 다음 답변을 찾았습니다 .

IIS 7 통합 파이프 라인 모드에서 실행중인 경우 다음을 입력하십시오 Global.asax.

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

참고 : 문제가 무엇인지 확인하기 위해 먼저 Request.ServerVariables실제로 포함 을 확인하는 IIS_WasUrlRewritten것이 좋습니다.


추신. 나는 이것이 나에게 발생하는 상황이 있다고 생각했고 src="~/content/..."내 HTML에 URL을 생성하고 있었지만 내 코드를 컴파일 할 때 뭔가 새로 고쳐지지 않은 것으로 나타났습니다. 레이아웃 및 페이지 cshtml 파일을 편집하고 다시 저장하면 어떻게 든 작동이 시작되었습니다.


6

ASP.NET에서는 일반적으로 <img src='<%= VirtualPathUtility.ToAbsolute("~/images/logo.gif") %>' alt="Our Company Logo"/>. 비슷한 솔루션이 ASP.NET MVC에서 작동하지 않는 이유를 모르겠습니다.


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

내가 사용한 것입니다. 예제와 일치하도록 경로를 변경하십시오.


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

Chris처럼, 나는 어리석은 일을 루트에서 위로 보라고 순전히 말하기 위해 내 깨끗한 마크 업 안에 부풀어 오른 서버 측 태그를 넣을 필요가 없습니다. 요청하기에는 매우 간단하고 합리적인 것이어야합니다. 그러나 나는 또한 이러한 간단한 일을하기 위해 커스텀 C # 클래스를 작성해야한다는 생각이 싫다. 왜 그래야만 하는가? 시간 낭비입니다.

저에게는 단순히 "완벽 함"을 타협하고 내 경로 참조 내에서 가상 디렉터리의 루트 경로 이름을 하드 코딩했습니다. 그래서 이렇게 :

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

URL을 확인하는 데 서버 측 처리 또는 C # 코드가 필요하지 않습니다. 이는 성능에 가장 적합하지만 무시해도 될 것입니다. 그리고 내 멋진 마크 업에서 부풀어 오른 추악한 서버 측 혼란이 없습니다.

나는 이것이 하드 코딩되고 http : // MyDevServer / MyProject / 대신 적절한 도메인으로 마이그레이션 될 때 제거되어야한다는 것을 알고 있어야합니다 .

건배


1
나는 당신을 0으로 되돌리기 위해 투표했습니다. 당신의 감정에 전적으로 동의합니다. 저는 순수한 C #에서 5 년 만에 웹 개발자를 처음 접했고 스파게티 혼란의 참혹한 땅입니다.
Luke Puplett

중첩 된 웹 앱에 배포와 같은 작업을 수행해야 할 때까지는 허용 가능한 타협으로 보입니다. 리졸버 마크 업을 사용하면이 문제가 해결되지만 정적 링크가 끊어집니다. 예 : 내장 웹 서버에 대해 로컬로 빌드 한 다음 앱을 domain.com/myNewWebApp에 푸시합니다
plyawn 2011 년

이 생산 시나리오 많이 파괴됩니다
오스카 Duveborn에게

나는 확실히이 솔루션 같은 : 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";
    }
}

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.