题 Path.Combine for URL?


Path.Combine 很方便,但.NET框架中有类似的功能 网址

我正在寻找这样的语法:

Url.Combine("http://MyUrl.com/", "/Images/Image.jpg")

会返回:

"http://MyUrl.com/Images/Image.jpg"


1026
2017-12-16 21:42


起源


Flurl 包括一个 Url.Combine 做到这一点的方法。 - Todd Menier
实际上,//由网站或服务器的路由处理,而不是由浏览器处理。它会将您输入的内容发送到地址栏。这就是我们输入htp://而不是http://时出现问题的原因所以//可能会导致某些网站出现重大问题。我正在为一个处理特定网站的爬虫编写一个.dll,如果你在网址中有//则抛出一个404。 - Dave Gordon


答案:


Uri 有一个构造函数,应该为您执行此操作: new Uri(Uri baseUri, string relativeUri)

这是一个例子:

Uri baseUri = new Uri("http://www.contoso.com");
Uri myUri = new Uri(baseUri, "catalog/shownew.htm");

1006
2017-10-06 19:37



我喜欢使用Uri类,不幸的是它不会像OP那样表现得像Path.Combine。例如新的Uri(新的Uri(“test.com/mydirectory/”),“/ helloworld.aspx”)。ToString()给你“test.com/helloworld.aspx”;;如果我们想要一个Path.Combine样式结果,这将是不正确的。 - Doctor Jones
一切都在斜线中。如果相对路径部分以斜杠开头,那么它的行为与您所描述的相同。但是,如果你留下斜线,那么它会按照你期望的方式工作(注意第二个参数上缺少的斜线):new Uri(new Uri(“test.com/mydirectory/”),“helloworld.aspx”)。ToString()导致“test.com/mydirectory/helloworld.aspx”;。 Path.Combine表现相似。如果相对路径参数以斜杠开头,则它仅返回相对路径并且不合并它们。 - Joel Beckham
如果您的baseUri恰好是“test.com/mydirectory/mysubdirectory”,那么结果将是“test.com/mydirectory/helloworld.aspx”而不是“test.com/mydirectory/mysubdirectory/helloworld.aspx”。细微差别是第一个参数缺少尾部斜杠。我只是使用现有的框架方法,如果我必须在那里使用尾随斜杠那么我认为做partUrl1 + partUrl2的气味要少得多 - 我可能已经在追逐那个尾随斜线了很长一段时间为了不做字符串连接。 - Carl
我想要一个URI组合方法的唯一原因是我不必检查尾部斜杠。如果您的应用程序位于根目录,则Request.ApplicationPath为“/”,如果不是,则为“/ foo”。 - nickd
我-1这个答案,因为这不能解决问题。当你想要组合url时,就像你想使用Path.Combine一样,你不想关心尾随/。有了这个,你必须要关心。我更喜欢上面的Brian MacKay或mdsharpe解决方案 - Baptiste Pernet


你用 Uri.TryCreate( ... ) :

Uri result = null;

if (Uri.TryCreate(new Uri("http://msdn.microsoft.com/en-us/library/"), "/en-us/library/system.uri.trycreate.aspx", out result))
{
    Console.WriteLine(result);
}

将返回:

http://msdn.microsoft.com/en-us/library/system.uri.trycreate.aspx


130
2017-12-16 21:49



+1:这很好,虽然我对输出参数有一个不合理的问题。 ;) - Brian MacKay
@Brian:如果有帮助,所有TryXXX方法(int.TryParse, DateTime.TryParseExact)有这个输出参数,以便在if语句中更容易使用它们。顺便说一下,你不必像Ryan在这个例子中那样初始化变量。 - Abel
这个答案遇到了同样的问题 乔尔的:加入 test.com/mydirectory/ 和 /helloworld.aspx 会导致 test.com/helloworld.aspx 这似乎不是你想要的。 - Matt Kocaj
嗨,这次失败了:if(Uri.TryCreate(new Uri)“本地主机/为MyService /”),“/ Event / SomeMethod?abc = 123”,out result)){Console.WriteLine(result);它显示了我的结果: 本地主机/活动/的someMethod?ABC = 123  注意:“http://”由stackoverflow从基础Uri替换 - Faisal Mq
@FaisalMq这是正确的行为,因为您传递了根相对的第二个参数。如果你遗漏了第二个参数的前导/上限,你就得到了预期的结果。 - Tom Lint


这可能是一个非常简单的解决方案:

public static string Combine(string uri1, string uri2)
{
    uri1 = uri1.TrimEnd('/');
    uri2 = uri2.TrimStart('/');
    return string.Format("{0}/{1}", uri1, uri2);
}

123
2017-09-25 10:29



+1:虽然这不处理相对风格的路径(../../whatever.html),但我喜欢这个简单的路径。我还会为'\'字符添加修剪。 - Brian MacKay
请参阅我的回答,了解更完整的版本。 - Brian MacKay


这里已经有了一些很棒的答案。基于mdsharpe建议,这里是一个扩展方法,可以在想要处理Uri实例时轻松使用:

using System;
using System.Linq;

public static class UriExtensions
{
    public static Uri Append(this Uri uri, params string[] paths)
    {
        return new Uri(paths.Aggregate(uri.AbsoluteUri, (current, path) => string.Format("{0}/{1}", current.TrimEnd('/'), path.TrimStart('/'))));
    }
}

用法示例:

var url = new Uri("http://example.com/subpath/").Append("/part1/", "part2").AbsoluteUri;

这将产生 http://example.com/subpath/part1/part2


106
2017-11-03 10:20



这个解决方案使得编写与Path.Combine()非常相似的UriUtils.Combine(“base url”,“part1”,“part2”,...)静态方法变得微不足道。太好了! - angularsen
为了支持相对URI,我必须在Uri构造函数中使用ToString()而不是AbsoluteUri和UriKind.AbsoluteOrRelative。 - angularsen
感谢关于相对Uris的提示。不幸的是,Uri并不容易处理相对路径,因为涉及到Request.ApplicationPath总是存在一些问题。也许您也可以尝试使用新的Uri(HttpContext.Current.Request.ApplicationPath)作为基础,只需在其上调用Append?这将为您提供绝对路径,但应在站点结构内的任何位置工作。 - Ales Potocnik Hahonina
大。很高兴它帮助了别人。已经使用了一段时间了,并没有任何问题。 - Ales Potocnik Hahonina
我还添加了检查是否有任何追加的路径不是null也不是空字符串。 - n.podbielski


这个问题得到了一些很好的,高度投票的答案!

Ryan Cook的答案接近我所追求的,可能更适合其他开发人员。但是,它将http://添加到字符串的开头,并且通常它比我之后的格式更多。

另外,对于我的用例,解析相对路径并不重要。

mdsharp的答案也包含了一个好主意的种子,尽管实际的实现需要更多细节才能完成。这是尝试充实它(我在生产中使用它):

C#

public string UrlCombine(string url1, string url2)
{
    if (url1.Length == 0) {
        return url2;
    }

    if (url2.Length == 0) {
        return url1;
    }

    url1 = url1.TrimEnd('/', '\\');
    url2 = url2.TrimStart('/', '\\');

    return string.Format("{0}/{1}", url1, url2);
}

VB.Net

Public Function UrlCombine(ByVal url1 As String, ByVal url2 As String) As String
    If url1.Length = 0 Then
        Return url2
    End If

    If url2.Length = 0 Then
        Return url1
    End If

    url1 = url1.TrimEnd("/"c, "\"c)
    url2 = url2.TrimStart("/"c, "\"c)

    Return String.Format("{0}/{1}", url1, url2)
End Function

此代码通过以下测试,恰好在VB中:

<TestMethod()> Public Sub UrlCombineTest()
    Dim target As StringHelpers = New StringHelpers()

    Assert.IsTrue(target.UrlCombine("test1", "test2") = "test1/test2")
    Assert.IsTrue(target.UrlCombine("test1/", "test2") = "test1/test2")
    Assert.IsTrue(target.UrlCombine("test1", "/test2") = "test1/test2")
    Assert.IsTrue(target.UrlCombine("test1/", "/test2") = "test1/test2")
    Assert.IsTrue(target.UrlCombine("/test1/", "/test2/") = "/test1/test2/")
    Assert.IsTrue(target.UrlCombine("", "/test2/") = "/test2/")
    Assert.IsTrue(target.UrlCombine("/test1/", "") = "/test1/")
End Sub

77
2018-05-10 21:53



谈论细节:强制性的 ArgumentNullException("url1") 如果论证是 Nothing?对不起,只是挑剔;-)。请注意,反斜杠在URI中无关(如果它在那里,则不应该进行修剪),因此您可以从TrimXXX中删除它。 - Abel
你可以使用params string []并递归地连接它们以允许超过2种组合 - Jaider
我当然希望这是在Path.Combine这样的基类库中。 - Uriah Blatherwick
@MarkHurd我再次编辑了代码,因此它的行为与C#相同,并且在语法上也是等价的。 - JJS
@BrianMacKay我打破了它,markhurd指出我的错误并回滚,我再次更新...欢呼 - JJS


基于样本 网址 您提供,我将假设您要组合相对于您的网站的网址。

根据这个假设,我会建议这个解决方案作为对你的问题的最恰当的回答:“Path.Combine很方便,有没有 类似的功能 在URL框架中?“

既然有一个 类似的功能 在我提出的URL框架中,正确的是:“VirtualPathUtility.Combine”方法。 这是MSDN参考链接: VirtualPathUtility.Combine方法

有一点需要注意:我认为这仅适用于相对于您网站的网址(也就是说,您无法使用它来生成指向其他网站的链接。例如, var url = VirtualPathUtility.Combine("www.google.com", "accounts/widgets");)。


32
2018-03-28 00:21



+1因为它接近我正在寻找的东西,尽管如果它适用于任何旧网址将是理想的。我加倍它将比mdsharpe提出的要优雅得多。 - Brian MacKay
警告是正确的,它不能用绝对的uris工作,结果总是相对于根。但它有一个额外的好处,它处理波浪号,就像“〜/”。这使它成为一种捷径 Server.MapPath 并结合。 - Abel


Path.Combine对我不起作用,因为可能有像“|”这样的字符在QueryString参数中,因此在Url中,这将导致ArgumentException。

我首先尝试了新的Uri(Uri baseUri,字符串relativeUri)方法,但由于Uri的喜欢而失败了 http://www.mediawiki.org/wiki/Special:SpecialPages

new Uri(new Uri("http://www.mediawiki.org/wiki/"), "Special:SpecialPages")

将导致Special:SpecialPages,因为Special之后的冒号表示一个方案。

所以我最终不得不采用mdsharpe / Brian MacKays路线并进一步开发它以使用多个uri部件:

public static string CombineUri(params string[] uriParts)
{
    string uri = string.Empty;
    if (uriParts != null && uriParts.Count() > 0)
    {
        char[] trims = new char[] { '\\', '/' };
        uri = (uriParts[0] ?? string.Empty).TrimEnd(trims);
        for (int i = 1; i < uriParts.Count(); i++)
        {
            uri = string.Format("{0}/{1}", uri.TrimEnd(trims), (uriParts[i] ?? string.Empty).TrimStart(trims));
        }
    }
    return uri;
}

用法: CombineUri("http://www.mediawiki.org/", "wiki", "Special:SpecialPages")


27
2017-07-15 08:17



+1:现在我们正在谈论......我要试试这个。这甚至可能最终成为新接受的答案。尝试新的Uri()方法后,我真的不喜欢它。太笨拙了。 - Brian MacKay
这正是我所需要的!我不喜欢关心拖尾斜线等的地方...... - Gromer
在空值检查中滚动+1,这样它就不会爆炸。 - NightOwl888


Path.Combine("Http://MyUrl.com/", "/Images/Image.jpg").Replace("\\", "/")

21
2018-01-11 21:41



path.Replace(Path.DirectorySeparatorChar, '/'); - Jaider
path.Replace(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar) - SliverNinja - MSFT
为了得到它你必须删除第一个/第二个arg即“/ Images” - / Path.Combine(“Http://MyUrl.com/”;,“Images / Image.jpg”) - Per G
@SliverNinja这不正确 此字段的值是UNIX上的反斜杠('\'),Windows和Macintosh操作系统上的斜杠('/')。 在Linux系统上使用Mono时,你会得到错误的分隔符。 - Stijn
所有在目录分隔符上显示的yall都忘记了字符串可能来自与现在不同的操作系统。只需用正斜杠替换反斜杠就可以了。 - JeremyWeir


我只是把小扩展方法放在一起

public static string UriCombine (this string val, string append)
        {
            if (String.IsNullOrEmpty(val)) return append;
            if (String.IsNullOrEmpty(append)) return val;
            return val.TrimEnd('/') + "/" + append.TrimStart('/');
        }

可以像这样使用:

"www.example.com/".UriCombine("/images").UriCombine("first.jpeg");

14
2017-11-25 08:43





很有趣的例子,Ryan,以这个功能的链接结束。做得好。

一个建议Brian:如果你将这段代码包装在一个函数中,你可能想要在TryCreate调用之前使用UriBuilder来包装基本URL。

否则,基本URL必须包含该方案(UriBuilder将采用http://)。只是一个想法:

public string CombineUrl(string baseUrl, string relativeUrl) {
    UriBuilder baseUri = new UriBuilder(baseUrl);
    Uri newUri;

    if (Uri.TryCreate(baseUri.Uri, relativeUrl, out newUri))
        return newUri.ToString();
    else
        throw new ArgumentException("Unable to combine specified url values");
}

11
2017-07-30 15:08





组合Url的多个部分可能有点棘手。您可以使用2参数构造函数 Uri(baseUri, relativeUri),或者你可以使用 Uri.TryCreate() 实用功能。无论哪种情况,您最终都可能返回不正确的结果,因为这些方法会不断截断第一个参数的相对部分 baseUri,即从类似的东西 http://google.com/some/thing 至 http://google.com

为了能够将多个部分组合成最终的URL,您可以复制以下两个函数:

    public static string Combine(params string[] parts)
    {
        if (parts == null || parts.Length == 0) return string.Empty;

        var urlBuilder = new StringBuilder();
        foreach (var part in parts)
        {
            var tempUrl = tryCreateRelativeOrAbsolute(part);
            urlBuilder.Append(tempUrl);
        }
        return VirtualPathUtility.RemoveTrailingSlash(urlBuilder.ToString());
    }

    private static string tryCreateRelativeOrAbsolute(string s)
    {
        System.Uri uri;
        System.Uri.TryCreate(s, UriKind.RelativeOrAbsolute, out uri);
        string tempUrl = VirtualPathUtility.AppendTrailingSlash(uri.ToString());
        return tempUrl;
    }

可以在以下位置找到用于演示用法的单元测试的完整代码 https://uricombine.codeplex.com/SourceControl/latest#UriCombine/Uri.cs

我有单元测试来涵盖3种最常见的情况: enter image description here


8
2018-04-30 22:21



看起来对我很好。虽然您可以使用foreach循环替换I循环以获得更好的清晰度。 - Chris Marisic
谢谢克里斯。我刚刚更改了代码以使用Foreach。 - Believe2014
为所有额外的努力+1。我需要对一些较高的投票答案保持这个问题,你已经抛下了挑战。 ;) - Brian MacKay
对不起,但还不够。适用于您展示的少数情况,但远非可用于其他组合。例如,路径中的冒号会造成伤害。 - Gábor
你能举个例子来说明一下你的意思吗?我很乐意解决这个问题并帮助下一个用户。 - Believe2014