从相对路径获取绝对路径(重构方法)

64

我非常惊讶地发现,.NET 没有本地方法可以从相对 URL 获取绝对 URL。我知道这个问题已经讨论过很多次了,但是还没有找到一种令人满意的方法来处理这个问题。你能帮忙优化下面的方法吗?

我认为我所需要的只是自动选择协议而不是硬编码它(http/https)。还有其他需要注意的地方吗(警告、性能等)?

public static string GetAbsoluteUrl(string url)
    {
        //VALIDATE INPUT FOR ALREADY ABSOLUTE URL
        if (url.StartsWith("http://", StringComparison.OrdinalIgnoreCase) 
           || url.StartsWith("https://", StringComparison.OrdinalIgnoreCase))
        { 
            return url;
        }

        //GET PAGE REFERENCE FOR CONTEXT PROCESSING
        Page page = HttpContext.Current.Handler as Page;

        //RESOLVE PATH FOR APPLICATION BEFORE PROCESSING
        if (url.StartsWith("~/"))
        {
            url = page.ResolveUrl(url);
        }

        //BUILD AND RETURN ABSOLUTE URL
        return "http://" + page.Request.ServerVariables["SERVER_NAME"] + "/" 
                         + url.TrimStart('/');
    }

1
你的代码有一个小建议。你应该使用String.Format而不是在结尾处连接URL片段。 - Bernd
11个回答

97

这一直是我处理这个小麻烦的方式。请注意使用VirtualPathUtility.ToAbsolute(relativeUrl)使方法能够在静态类中声明为扩展。

/// <summary>
/// Converts the provided app-relative path into an absolute Url containing the 
/// full host name
/// </summary>
/// <param name="relativeUrl">App-Relative path</param>
/// <returns>Provided relativeUrl parameter as fully qualified Url</returns>
/// <example>~/path/to/foo to http://www.web.com/path/to/foo</example>
public static string ToAbsoluteUrl(this string relativeUrl) {
    if (string.IsNullOrEmpty(relativeUrl))
        return relativeUrl;

    if (HttpContext.Current == null)
        return relativeUrl;

    if (relativeUrl.StartsWith("/"))
        relativeUrl = relativeUrl.Insert(0, "~");
    if (!relativeUrl.StartsWith("~/"))
        relativeUrl = relativeUrl.Insert(0, "~/");

    var url = HttpContext.Current.Request.Url;
    var port = url.Port != 80 ? (":" + url.Port) : String.Empty;

    return String.Format("{0}://{1}{2}{3}",
        url.Scheme, url.Host, port, VirtualPathUtility.ToAbsolute(relativeUrl));
}

2
快速提醒..有时我不想要路径的根目录,而是想要一个到达我的位置的根目录。就像这样:Page.ResolveUrl("SomePage.aspx")。在你的方法中,这将假定我想要/SomePage.aspx,但实际上我想要/path/to/where/I/currently/am/SomePage.aspx。非常好的方法! - TruMan1
顺便提一句,VirtualPathUtility.ToAbsolute 要求你在开头加上 ~ 符号。但是如果我需要一个相对路径来指向我的位置,那么 (context.Handler as Page).ResolveUrl 就可以使用了。 - TruMan1
3
很棒的方法,只需要做一个小修改,当它在使用https协议时不要添加端口号。修改如下: "var port = url.Port == 80 || (url.Scheme == "https" && url.Port == 443) ? "" : ":" + url.Port;" - Paleta
你真的需要修改示例,以添加@Paleta评论中的代码。 - JJS
4
旧帖子,但我认为最好使用 url.IsDefaultPort 而不是检查 http 和 https 实例的端口号。 - goelze
显示剩余3条评论

73
new System.Uri(Page.Request.Url, "/myRelativeUrl.aspx").AbsoluteUri

1
谢谢。至少对于Windows Store应用程序来说,这很有效。 - Hong
我认为这应该是现在的答案了... 对于Web应用程序也非常有效。 - Nirman
如果提供的相对URL实际上是绝对URL,那么似乎会失败。例如,new System.Uri("http://example.com", "http://google.com")应该返回Google链接。 - Nacht
1
我不同意。当相对URL是绝对的时,这也适用。但是,如果你的相对URL中有“~”,它就不起作用了。 - wezzix

15

这个对我有效...

new System.Uri(Page.Request.Url, ResolveClientUrl("~/mypage.aspx")).AbsoluteUri

6

在使用ASP.NET时,需要考虑"相对URL"的参考点 - 是相对于页面请求、用户控件,还是仅通过使用"~/"而是"相对"的?

Uri类包含了一种简单的方法来将相对URL转换为绝对URL(给定一个绝对URL作为相对URL的参考点):

var uri = new Uri(absoluteUrl, relativeUrl);

如果relativeUrl实际上是绝对URL,则忽略absoluteUrl
唯一的问题是参考点在哪里以及是否允许"~/" URL(Uri构造器不会翻译这些内容)。

4
这是我自己编写的版本,可以处理许多验证和用户当前位置相关的路径选项。你可以从这里进行重构 :)
/// <summary>
/// Converts the provided app-relative path into an absolute Url containing 
/// the full host name
/// </summary>
/// <param name="relativeUrl">App-Relative path</param>
/// <returns>Provided relativeUrl parameter as fully qualified Url</returns>
/// <example>~/path/to/foo to http://www.web.com/path/to/foo</example>
public static string GetAbsoluteUrl(string relativeUrl)
{
    //VALIDATE INPUT
    if (String.IsNullOrEmpty(relativeUrl))
        return String.Empty;
    //VALIDATE INPUT FOR ALREADY ABSOLUTE URL
    if (relativeUrl.StartsWith("http://", StringComparison.OrdinalIgnoreCase) 
    || relativeUrl.StartsWith("https://", StringComparison.OrdinalIgnoreCase))
        return relativeUrl;
    //VALIDATE CONTEXT
    if (HttpContext.Current == null)
        return relativeUrl;
    //GET CONTEXT OF CURRENT USER
    HttpContext context = HttpContext.Current;
    //FIX ROOT PATH TO APP ROOT PATH
    if (relativeUrl.StartsWith("/"))
        relativeUrl = relativeUrl.Insert(0, "~");
    //GET RELATIVE PATH
    Page page = context.Handler as Page;
    if (page != null)
    {
        //USE PAGE IN CASE RELATIVE TO USER'S CURRENT LOCATION IS NEEDED
        relativeUrl = page.ResolveUrl(relativeUrl);
    }
    else //OTHERWISE ASSUME WE WANT ROOT PATH
   {
        //PREPARE TO USE IN VIRTUAL PATH UTILITY
        if (!relativeUrl.StartsWith("~/"))
            relativeUrl = relativeUrl.Insert(0, "~/");
        relativeUrl = VirtualPathUtility.ToAbsolute(relativeUrl);
    }

    var url = context.Request.Url;
    var port = url.Port != 80 ? (":" + url.Port) : String.Empty;
    //BUILD AND RETURN ABSOLUTE URL
    return String.Format("{0}://{1}{2}{3}",
           url.Scheme, url.Host, port, relativeUrl);
}

4

如果您在MVC控制器或视图的上下文中,可以使用UrlHelper,只需通过Url访问即可。

Url.Content("~/content/images/myimage.jpg")

这将完全扩展为 /virtual_directoryname/content/images/myimage.jpg

这可以在控制器或 .cshtml 文件中使用

是的,它被称为Content有点奇怪,但它旨在用于获取资源的绝对路径,因此很有意义


1
这不是一个绝对URL。 - Mahmoud Al-Qudsi
正确。这是根相对URL。 - Simon_Weaver

1

仍然没有使用本地工具找到足够好的东西。这是我最终得出的结果:

public static string GetAbsoluteUrl(string url)
{
    //VALIDATE INPUT
    if (String.IsNullOrEmpty(url))
    {
        return String.Empty;
    }

    //VALIDATE INPUT FOR ALREADY ABSOLUTE URL
    if (url.StartsWith("http://", StringComparison.OrdinalIgnoreCase) || url.StartsWith("https://", StringComparison.OrdinalIgnoreCase))
    { 
        return url;
    }

    //GET CONTEXT OF CURRENT USER
    HttpContext context = HttpContext.Current;

    //RESOLVE PATH FOR APPLICATION BEFORE PROCESSING
    if (url.StartsWith("~/"))
    {
        url = (context.Handler as Page).ResolveUrl(url);
    }

    //BUILD AND RETURN ABSOLUTE URL
    string port = (context.Request.Url.Port != 80 && context.Request.Url.Port != 443) ? ":" + context.Request.Url.Port : String.Empty;
    return context.Request.Url.Scheme + Uri.SchemeDelimiter + context.Request.Url.Host + port + "/" + url.TrimStart('/');
}

1
当您想要从业务逻辑层生成URL时,您没有使用ASP.NET Web Form的Page类/Control的ResolveUrl(..)等灵活性。此外,您可能还需要从ASP.NET MVC控制器生成URL,在那里您不仅错过了Web表单的ResolveUrl(..)方法,而且即使Url.Action只需要控制器名称和操作名称而不是相对URL,但您也无法获得Url.Action(..)。
我尝试使用“var uri = new Uri(absoluteUrl, relativeUrl)”方法,但也存在问题。如果Web应用程序托管在IIS虚拟目录中,其中应用程序的URL如下:“http://localhost/MyWebApplication1/”,而相对URL为“/myPage”,则相对URL将解析为“http://localhost/MyPage”,这是另一个问题。
因此,为了克服这些问题,我编写了一个UrlUtils类,可以从一个类库中工作。所以,它不依赖于Page类,但是依赖于ASP.NET MVC。如果您不介意将MVC dll引用到您的类库项目中,那么我的类将能够顺利工作。我已经在IIS虚拟目录场景中进行了测试,其中Web应用程序url如下:http://localhost/MyWebApplication/MyPage。我意识到,有时我们需要确保绝对url是SSL url或非SSL url。因此,我编写了支持此选项的类库。我限制了这个类库,使得相对url可以是绝对url或以“~/”开头的相对url。

使用这个库,我可以调用

string absoluteUrl = UrlUtils.MapUrl("~/Contact");

当页面url为http://localhost/Home/About时,返回:http://localhost/Contact 当页面url为http://localhost/MyWebApplication/Home/About时,返回:http://localhost/MyWebApplication/Contact
  string absoluteUrl = UrlUtils.MapUrl("~/Contact", UrlUtils.UrlMapOptions.AlwaysSSL);

返回结果:

当页面url为:http://localhost/MyWebApplication/Home/About时,返回:**https**://localhost/MyWebApplication/Contact

这是我的类库:

 public class UrlUtils
    {
        public enum UrlMapOptions
        {
            AlwaysNonSSL,
            AlwaysSSL,
            BasedOnCurrentScheme
        }

        public static string MapUrl(string relativeUrl, UrlMapOptions option = UrlMapOptions.BasedOnCurrentScheme)
        {
            if (relativeUrl.StartsWith("http://", StringComparison.OrdinalIgnoreCase) ||
                relativeUrl.StartsWith("https://", StringComparison.OrdinalIgnoreCase))
                return relativeUrl;

            if (!relativeUrl.StartsWith("~/"))
                throw new Exception("The relative url must start with ~/");

            UrlHelper theHelper = new UrlHelper(HttpContext.Current.Request.RequestContext);

            string theAbsoluteUrl = HttpContext.Current.Request.Url.GetLeftPart(UriPartial.Authority) +
                                           theHelper.Content(relativeUrl);

            switch (option)
            {
                case UrlMapOptions.AlwaysNonSSL:
                    {
                        return theAbsoluteUrl.StartsWith("https://", StringComparison.OrdinalIgnoreCase)
                            ? string.Format("http://{0}", theAbsoluteUrl.Remove(0, 8))
                            : theAbsoluteUrl;
                    }
                case UrlMapOptions.AlwaysSSL:
                    {
                        return theAbsoluteUrl.StartsWith("https://", StringComparison.OrdinalIgnoreCase)
                            ? theAbsoluteUrl
                            : string.Format("https://{0}", theAbsoluteUrl.Remove(0, 7));
                    }
            }

            return theAbsoluteUrl;
        }
    }   

1

最终版本解决了之前所有的投诉(端口、逻辑URL、相对URL、已存在的绝对URL等),考虑到当前处理程序是页面:

public static string ConvertToAbsoluteUrl(string url)
{
    if (!IsAbsoluteUrl(url))
    {
        if (HttpContext.Current != null && HttpContext.Current.Request != null && HttpContext.Current.Handler is System.Web.UI.Page)
        {
            var originalUrl = HttpContext.Current.Request.Url;
            return string.Format("{0}://{1}{2}{3}", originalUrl.Scheme, originalUrl.Host, !originalUrl.IsDefaultPort ? (":" + originalUrl.Port) : string.Empty, ((System.Web.UI.Page)HttpContext.Current.Handler).ResolveUrl(url));
        }
        throw new Exception("Invalid context!");
    }
    else
        return url;
}

private static bool IsAbsoluteUrl(string url)
{
    Uri result;
    return Uri.TryCreate(url, UriKind.Absolute, out result);
}

0
请查看以下代码以检索绝对 URL:
Page.Request.Url.AbsoluteUri

希望能够有所帮助。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接