URL的Path.Combine吗?


1242

Path.Combine很方便,但是.NET框架中有类似的URL功能吗?

我正在寻找这样的语法:

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

这将返回:

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


14
Flurl包括一种Url.Combine可以做到这一点的方法。
Todd Menier 2014年

2
实际上,//是通过网站或服务器的路由而不是浏览器来处理的。它将发送您放入地址栏中的内容。这就是为什么在键入htp://而不是http://时会出现问题,因此//可能在某些站点上引起严重问题。我正在为抓取工具编写一个.dll,该抓取工具可以处理特定的网站,如果您在网址中//则抛出404。
Dave Gordon

Answers:


73

上面有一个Todd Menier的评论Flurl包含一个Url.Combine

更多细节:

Url.Combine本质上是URL的Path.Combine,确保部分之间只有一个分隔符:

var url = Url.Combine(
    "http://MyUrl.com/",
    "/too/", "/many/", "/slashes/",
    "too", "few?",
    "x=1", "y=2"
// result: "http://www.MyUrl.com/too/many/slashes/too/few?x=1&y=2" 

在NuGet上获取Flurl.Http

PM>安装包Flurl.Http

获取不具有HTTP功能的独立URL构建器

PM>安装包Flurl


4
嗯,这个问题获得了大量流量,并且在所有情况下都无法获得1000票以上的答案。多年之后,我实际上使用Flurl来完成这项工作,因此我接受了这一点。它似乎在我遇到的所有情况下都有效。如果人们不想依赖,我发布了一个也可以正常工作的答案。
Brian MacKay

如果您不使用Flurl并推荐使用轻量级版本,请访问github.com/jean-lourenco/UrlCombine
lizzy91

1156

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

这是一个例子:

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

编辑注意:请注意,此方法无法正常工作。在某些情况下,它可以削减baseUri的一部分。查看评论和其他答案。


369
我喜欢使用Uri类,不幸的是,它不会像OP要求的那样表现为Path.Combine。例如new Uri(new Uri(“ test.com/mydirectory/”),“ /helloworld.aspx”)。ToString()为您提供“ test.com/helloworld.aspx ”; 如果我们想要Path.Combine样式的结果,那将是不正确的。
琼斯医生

195
都在斜线中。如果相对路径部分以斜杠开头,则其行为与您描述的相同。但是,如果您不使用斜杠,那么它将按照您期望的方式工作(请注意第二个参数上缺少的斜杠):new Uri(new Uri(“ test.com/mydirectory/”),“ helloworld.aspx” ).ToString()生成“ test.com/mydirectory/helloworld.aspx ”。Path.Combine的行为类似。如果相对路径参数以斜杠开头,则仅返回相对路径,而不将它们组合在一起。
乔·贝克汉姆

70
如果您的baseUri恰好是“ test.com/mydirectory/mysubdirectory”,则结果将是“ test.com/mydirectory/helloworld.aspx”,而不是“ test.com/mydirectory/mysubdirectory/helloworld.aspx”。细微的差别是第一个参数没有尾部斜杠。我全都使用现有的框架方法,如果我必须已经在其中加了斜杠,那么我认为做partUrl1 + partUrl2的气味要少得多-我可能已经在相当长的一段时间内一直跟踪该斜杠了为了不做字符串连接。
卡尔

63
我想要URI组合方法的唯一原因是,我不必检查结尾的斜杠。如果您的应用程序位于根目录,则Request.ApplicationPath为'/',否则为'/ foo'。
2011年

24
我-1这个答案,因为这不能解决问题。当您想要组合url时(例如,当您要使用Path.Combine时),您不必在意/。有了这个,你必须要关心。我喜欢布赖恩·麦凯或mdsharpe上述解决方案
巴蒂斯特Pernet女士

160

这可能是一个适当的简单解决方案:

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

7
+1:虽然这不能处理相对样式的路径(../../whatever.html),但我还是喜欢它的简单性。我还将为'\'字符添加修饰。
Brian MacKay 2010年

3
请参阅我的答案,以获取更完整的版本。
Brian MacKay'5

149

您使用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


53
+1:很好,尽管我在输出参数上有一个不合理的问题。;)
Brian MacKay

10
@布赖恩:如果有帮助,所有TryXXX方法(int.TryParseDateTime.TryParseExact)有这样的输出参数,使其更容易在if语句中使用它们。顺便说一句,您不必像本例中Ryan那样初始化变量。
亚伯

41
这个答案与Joel's存在相同的问题:加入test.com/mydirectory//helloworld.aspx导致test.com/helloworld.aspx看起来似乎不是您想要的。
Matt Kocaj

3
嗨,这失败了,因为以下原因:if(Uri.TryCreate(new Uri(“ localhost / MyService /”),“ / Event / SomeMethod?abc = 123”,超出结果))}它向我显示的结果为:localhost / Event / SomeMethod?abc = 123 注意:此处的基本Uri中的“ http://”已由stackoverflow取代
Faisal Mq

3
@FaisalMq这是正确的行为,因为您传递了相对于根的第二个参数。如果您在第二个参数上省略了前导/,那么您将获得预期的结果。
Tom Lint 2014年

127

这里已经有了一些很好的答案。根据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


2
此解决方案使得编写UriUtils.Combine(“ base url”,“ part1”,“ part2”,...)静态方法非常简单,该方法与Path.Combine()非常相似。真好!
angularsen 2011年

为了支持相对URI,我必须在Uri构造函数中使用ToString()代替AbsoluteUri和UriKind.AbsoluteOrRelative。
angularsen 2011年

感谢有关相对Uris的提示。不幸的是,Uri并不容易处理相对路径,因为其中总是涉及到Request.ApplicationPath的问题。也许您也可以尝试使用新的Uri(HttpContext.Current.Request.ApplicationPath)作为基础,然后在其上调用Append?这将为您提供绝对路径,但应可在站点结构内的任何地方工作。
Ales Potocnik Hahonina

大。很高兴它帮助了其他人。现在已经使用了一段时间,没有任何问题。
Ales Potocnik Hahonina

我还添加了检查是否要追加的任何路径都不为null或为空字符串。
n.podbielski '16

91

瑞安·库克(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

4
谈论细节:ArgumentNullException("url1")如果参数为,那么强制性的Nothing呢?抱歉,很挑剔;-)。请注意,反斜杠与URI无关(如果存在,则不应修剪),因此可以从TrimXXX中将其删除。
亚伯

4
您可以使用params string []并将其递归加入以允许2种以上的组合
Jaider 2012年

4
我当然希望这在Path.Combine之类的基类库中。
Uriah Blatherwick 2014年

1
@MarkHurd我再次编辑了代码,因此它在行为上与C#相同,并且在语法上也等效。
JJS

1
@BrianMacKay我打破了它,Markhurd指出了我的错误并回滚了,我再次更新了...干杯
JJS 2016年

36

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

我首先尝试了新Uri(Uri baseUri, string 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.Length > 0)
    {
        char[] trims = new char[] { '\\', '/' };
        uri = (uriParts[0] ?? string.Empty).TrimEnd(trims);
        for (int i = 1; i < uriParts.Length; 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")


1
+1:现在我们正在谈论...我将尝试一下。这甚至可能最终成为新的接受答案。在尝试了新的Uri()方法之后,我真的不喜欢它。太挑剔了。
Brian MacKay

这正是我所需要的!不喜欢在我后面加斜杠等地方的爱好者……
Gromer 2012年

+1用于滚动null检查,因此不会崩溃。
NightOwl888 2014年

Count()应该为Length,这样就不必为此在您的库中包括Linq。
PRMan

这正是我在寻找的东西。
ThePeter

34

根据您提供的示例URL,我将假设您要组合相对于您的网站的URL。

基于此假设,我将提出此解决方案作为对您的问题的最适当答复:“ Path.Combine很方便,URL框架中是否有类似的功能?”

由于URL框架中存在类似的功能,因此我建议正确的方法是:“ VirtualPathUtility.Combine”方法。这是MSDN参考链接:VirtualPathUtility.Combine方法

有一个警告:我相信这仅适用于相对于您网站的URL(也就是说,您不能使用它来生成指向另一个网站的链接。例如,var url = VirtualPathUtility.Combine("www.google.com", "accounts/widgets");)。


+1是因为它很接近我要寻找的内容,但是如果它适用于任何旧网址,那将是理想的选择。我加倍,它将比mdsharpe提出的要优雅得多。
Brian MacKay 2010年

2
警告是正确的,它不能绝对地起作用,并且结果始终是从根开始相对的。但是它还有一个额外的好处,它可以像使用“〜/”一样处理波浪号。这使其成为Server.MapPath组合的捷径。
亚伯

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

12
path.Replace(Path.DirectorySeparatorChar, '/');
贾德

5
path.Replace(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar)
SliverNinja-MSFT 2012年

1
要使其正常运行,您必须先删除第一个/第二个参数,即“ / Images”-/ Path.Combine(“ Http://MyUrl.com ”,“ Images / Image.jpg”)
Per G

8
@SliverNinja不正确此字段的值在UNIX上为反斜杠('\'),在Windows和Macintosh操作系统上为斜杠('/')。在Linux系统上使用Mono时,会得到错误的分隔符。
user247702 2014年

6
所有在目录分隔符上讨厌的人都忘记了这些字符串可能来自与您现在不同的操作系统。只需将反斜杠替换为正斜杠就可以了。
JeremyWeir '16

17

我只是整理了一个小的扩展方法:

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

12

机智的例子Ryan以该函数的链接结尾。做得好。

建议Brian:如果将此代码包装在一个函数中,则可能需要使用UriBuilder在TryCreate调用之前包装基本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");
}

10

合并它们并确保其始终正确的一种简单方法是:

string.Format("{0}/{1}", Url1.Trim('/'), Url2);

+1,尽管这与mdsharpe的回答非常相似,但我在回答中对此有所改进。除非Url2以/或\开头,或者Url1意外以\结束,或者其中一个为空,否则此版本非常有用。:)
Brian MacKay'5

9

组合URL的多个部分可能会有些棘手。您可以使用两参数构造函数Uri(baseUri, relativeUri),也可以使用Uri.TryCreate()实用程序功能。

在这两种情况下,您都可能最终返回不正确的结果,因为这些方法会不断截断第一个参数的相对部分baseUri,例如从http://google.com/some/thingto到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中找到带有单元测试以演示用法的完整代码。

我的单元测试涵盖了三种最常见的情况:

在此处输入图片说明


2
对我来说看起来不错。尽管您可以用foreach循环替换I循环,以提高清晰度。
克里斯·马里西克

谢谢克里斯。我刚刚将代码更改为使用Foreach。
Believe2014 2014年

1
+1为所有额外的努力。对于某些投票率较高的答案,我需要保持一点疑问,您已经放弃了这个挑战。;)
Brian MacKay 2014年

抱歉,还不够。在您展示的少数情况下有效,但远不能组合使用。例如,路径中的冒号会造成伤害。
加博尔

你能举一个例子说明你的意思吗?我很乐意解决此问题并为下一个用户提供帮助。
Believe2014

7

我发现UriBuilder这种事情非常有效:

UriBuilder urlb = new UriBuilder("http", _serverAddress, _webPort, _filePath);
Uri url = urlb.Uri;
return url.AbsoluteUri;

有关更多构造函数和文档,请参见UriBuilder类-MSDN


4

这是Microsoft的(OfficeDev PnP)方法UrlUtility.Combine

    const char PATH_DELIMITER = '/';

    /// <summary>
    /// Combines a path and a relative path.
    /// </summary>
    /// <param name="path"></param>
    /// <param name="relative"></param>
    /// <returns></returns>
    public static string Combine(string path, string relative) 
    {
        if(relative == null)
            relative = String.Empty;

        if(path == null)
            path = String.Empty;

        if(relative.Length == 0 && path.Length == 0)
            return String.Empty;

        if(relative.Length == 0)
            return path;

        if(path.Length == 0)
            return relative;

        path = path.Replace('\\', PATH_DELIMITER);
        relative = relative.Replace('\\', PATH_DELIMITER);

        return path.TrimEnd(PATH_DELIMITER) + PATH_DELIMITER + relative.TrimStart(PATH_DELIMITER);
    }

资料来源:GitHub


看起来这可能是路径而不是URL。
Brian MacKay 2015年

@BrianMacKay同意它看起来像,但它来自UrlUtility类,用于结合URL的上下文中

2
编辑以澄清它属于哪个类

使用此类时请多加注意,该类的其余部分包含SharePoint特定的工件。
哈里·贝瑞

4

我发现以下有用并具有以下功能:

  • 抛出空白或空白
  • 需要多个 params为多个网址段参数
  • 抛出null或空

public static class UrlPath
{
   private static string InternalCombine(string source, string dest)
   {
      if (string.IsNullOrWhiteSpace(source))
         throw new ArgumentException("Cannot be null or white space", nameof(source));

      if (string.IsNullOrWhiteSpace(dest))
         throw new ArgumentException("Cannot be null or white space", nameof(dest));

      return $"{source.TrimEnd('/', '\\')}/{dest.TrimStart('/', '\\')}";
   }

   public static string Combine(string source, params string[] args) 
       => args.Aggregate(source, InternalCombine);
}

测验

UrlPath.Combine("test1", "test2");
UrlPath.Combine("test1//", "test2");
UrlPath.Combine("test1", "/test2");

// Result = test1/test2

UrlPath.Combine(@"test1\/\/\/", @"\/\/\\\\\//test2", @"\/\/\\\\\//test3\") ;

// Result = test1/test2/test3

UrlPath.Combine("/test1/", "/test2/", null);
UrlPath.Combine("", "/test2/");
UrlPath.Combine("/test1/", null);

// Throws an ArgumentException

@PeterMortensen感谢您的编辑
TheGeneral

测试的一些问题://第四个测试的结果= test1 / test2 / test3 \,最后一次引发测试给出ArgumentNullException而不是ArgumentException
Moriya

3

我的通用解决方案:

public static string Combine(params string[] uriParts)
{
    string uri = string.Empty;
    if (uriParts != null && uriParts.Any())
    {
        char[] trims = new char[] { '\\', '/' };
        uri = (uriParts[0] ?? string.Empty).TrimEnd(trims);

        for (int i = 1; i < uriParts.Length; i++)
        {
            uri = string.Format("{0}/{1}", uri.TrimEnd(trims), (uriParts[i] ?? string.Empty).TrimStart(trims));
        }
    }

    return uri;
}

这种辅助方法非常灵活,可以在许多不同的用例中很好地工作。谢谢!
湿婆神

3

我创建了此功能,可以使您的生活更轻松:

    /// <summary>
    /// The ultimate Path combiner of all time
    /// </summary>
    /// <param name="IsURL">
    /// true - if the paths are Internet URLs, false - if the paths are local URLs, this is very important as this will be used to decide which separator will be used.
    /// </param>
    /// <param name="IsRelative">Just adds the separator at the beginning</param>
    /// <param name="IsFixInternal">Fix the paths from within (by removing duplicate separators and correcting the separators)</param>
    /// <param name="parts">The paths to combine</param>
    /// <returns>the combined path</returns>
    public static string PathCombine(bool IsURL , bool IsRelative , bool IsFixInternal , params string[] parts)
    {
        if (parts == null || parts.Length == 0) return string.Empty;
        char separator = IsURL ? '/' : '\\';

        if (parts.Length == 1 && IsFixInternal)
        {
            string validsingle;
            if (IsURL)
            {
                validsingle = parts[0].Replace('\\' , '/');
            }
            else
            {
                validsingle = parts[0].Replace('/' , '\\');
            }
            validsingle = validsingle.Trim(separator);
            return (IsRelative ? separator.ToString() : string.Empty) + validsingle;
        }

        string final = parts
            .Aggregate
            (
            (string first , string second) =>
            {
                string validfirst;
                string validsecond;
                if (IsURL)
                {
                    validfirst = first.Replace('\\' , '/');
                    validsecond = second.Replace('\\' , '/');
                }
                else
                {
                    validfirst = first.Replace('/' , '\\');
                    validsecond = second.Replace('/' , '\\');
                }
                var prefix = string.Empty;
                if (IsFixInternal)
                {
                    if (IsURL)
                    {
                        if (validfirst.Contains("://"))
                        {
                            var tofix = validfirst.Substring(validfirst.IndexOf("://") + 3);
                            prefix = validfirst.Replace(tofix , string.Empty).TrimStart(separator);

                            var tofixlist = tofix.Split(new[] { separator } , StringSplitOptions.RemoveEmptyEntries);

                            validfirst = separator + string.Join(separator.ToString() , tofixlist);
                        }
                        else
                        {
                            var firstlist = validfirst.Split(new[] { separator } , StringSplitOptions.RemoveEmptyEntries);
                            validfirst = string.Join(separator.ToString() , firstlist);
                        }

                        var secondlist = validsecond.Split(new[] { separator } , StringSplitOptions.RemoveEmptyEntries);
                        validsecond = string.Join(separator.ToString() , secondlist);
                    }
                    else
                    {
                        var firstlist = validfirst.Split(new[] { separator } , StringSplitOptions.RemoveEmptyEntries);
                        var secondlist = validsecond.Split(new[] { separator } , StringSplitOptions.RemoveEmptyEntries);

                        validfirst = string.Join(separator.ToString() , firstlist);
                        validsecond = string.Join(separator.ToString() , secondlist);
                    }
                }
                return prefix + validfirst.Trim(separator) + separator + validsecond.Trim(separator);
            }
            );
        return (IsRelative ? separator.ToString() : string.Empty) + final;
    }

它适用于URL以及普通路径。

用法:

    // Fixes internal paths
    Console.WriteLine(PathCombine(true , true , true , @"\/\/folder 1\/\/\/\\/\folder2\///folder3\\/" , @"/\somefile.ext\/\//\"));
    // Result: /folder 1/folder2/folder3/somefile.ext

    // Doesn't fix internal paths
    Console.WriteLine(PathCombine(true , true , false , @"\/\/folder 1\/\/\/\\/\folder2\///folder3\\/" , @"/\somefile.ext\/\//\"));
    //result : /folder 1//////////folder2////folder3/somefile.ext

    // Don't worry about URL prefixes when fixing internal paths
    Console.WriteLine(PathCombine(true , false , true , @"/\/\/https:/\/\/\lul.com\/\/\/\\/\folder2\///folder3\\/" , @"/\somefile.ext\/\//\"));
    // Result: https://lul.com/folder2/folder3/somefile.ext

    Console.WriteLine(PathCombine(false , true , true , @"../../../\\..\...\./../somepath" , @"anotherpath"));
    // Result: \..\..\..\..\...\.\..\somepath\anotherpath

3

为什么不只使用以下内容。

System.IO.Path.Combine(rootUrl, subPath).Replace(@"\", "/")

我一直在寻找这样的PowerShell版本:[System.IO.Path]::Combine("http://MyUrl.com/","/Images/Image.jpg")但是,由于以下结果而失败/Images/Image.jpg/从第二个子路径中删除,即可使用:[System.IO.Path]::Combine("http://MyUrl.com/","Images/Image.jpg")
颠覆了

不错的主意,但是当参数之一为null时失败。
pholpar

2

将URL与URI结合时的规则

为避免发生奇怪的行为,请遵循以下规则:

  • 路径(目录)必须以“ /”结尾。如果路径以“ /”结尾,则将最后一部分视为文件名,并且在尝试与下一个URL部分结合时将其串联。
  • 有一个例外:基本URL地址(不带目录信息)不必以'/'结尾
  • 路径部分不能以“ /”开头。如果以'/'开头,string.Empty则会删除URL中所有现有的相对信息...添加部件路径也会从URL中删除相对目录!

如果您遵循上述规则,则可以将URL与以下代码结合使用。根据您的情况,您可以在URL中添加多个“目录”部分...

        var pathParts = new string[] { destinationBaseUrl, destinationFolderUrl, fileName };

        var destination = pathParts.Aggregate((left, right) =>
        {
            if (string.IsNullOrWhiteSpace(right))
                return left;

            return new Uri(new Uri(left), right).ToString();
        });

2

如果不想在ASP.NET Core(也可以在Microsoft.Owin中)中添加第三方依赖项(例如Flurl)或创建自定义扩展方法,则可以使用PathString它来建立URI。路径。然后,您可以使用这个组合创建完整的URI,UriUriBuilder

在这种情况下,它将是:

new Uri(new UriBuilder("http", "MyUrl.com").Uri, new PathString("/Images").Add("/Image.jpg").ToString())

这为您提供了所有组成部分,而无需在基本URL中指定分隔符。不幸的是,PathString要求/在每个字符串之前加上,否则实际上会抛出ArgumentException!。但是至少您可以以易于进行单元测试的方式确定性地构建URI。


2

因此,我有另一种方法,类似于使用UriBuilder的每个人。

我不想将我的BaseUrl(可以包含路径的一部分,例如http://mybaseurl.com/dev/拆分javajavajavajavajava一样。

以下代码段显示了代码+测试。

当心:此解决方案会小写主机并附加端口。如果不希望这样,可以通过例如利用Uri属性来编写字符串表示形式UriBuilder

  public class Tests
  {
         public static string CombineUrl (string baseUrl, string path)
         {
           var uriBuilder = new UriBuilder (baseUrl);
           uriBuilder.Path = Path.Combine (uriBuilder.Path, path);
           return uriBuilder.ToString();
         }

         [TestCase("http://MyUrl.com/", "/Images/Image.jpg", "http://myurl.com:80/Images/Image.jpg")]
         [TestCase("http://MyUrl.com/basePath", "/Images/Image.jpg", "http://myurl.com:80/Images/Image.jpg")]
         [TestCase("http://MyUrl.com/basePath", "Images/Image.jpg", "http://myurl.com:80/basePath/Images/Image.jpg")]
         [TestCase("http://MyUrl.com/basePath/", "Images/Image.jpg", "http://myurl.com:80/basePath/Images/Image.jpg")]
         public void Test1 (string baseUrl, string path, string expected)
         {
           var result = CombineUrl (baseUrl, path);

           Assert.That (result, Is.EqualTo (expected));
         }
  }

已在Windows 10上使用.NET Core 2.1进行测试。

为什么这样做?

即使Path.Combine将返回反斜杠(在Windows上至少),UriBuilder也会在Setter ofPath

取自https://github.com/dotnet/corefx/blob/master/src/System.Private.Uri/src/System/UriBuilder.cs(注意对的调用string.Replace

[AllowNull]
public string Path
{
      get
      {
          return _path;
      }
      set
      {
          if ((value == null) || (value.Length == 0))
          {
              value = "/";
          }
          _path = Uri.InternalEscapeString(value.Replace('\\', '/'));
          _changed = true;
      }
 }

这是最好的方法吗?

当然,此解决方案非常自我描述(至少在我看来)。但是,您依赖.NET API中未记录的功能(至少在Google谷歌快速搜索中没有找到任何内容)“功能”。将来的发行版中可能会有所变化,因此请涵盖“测试方法”。

https://github.com/dotnet/corefx/blob/master/src/System.Private.Uri/tests/FunctionalTests/UriBuilderTests.csPath_Get_Set)中有测试,检查是否\正确转换了。

旁注:UriBuilder.Uri如果uri将用作System.Urictor ,则也可以直接使用该属性。


这是一种非常可靠的方法。竖起大拇指进行单元测试!!
aggsol

2

对于正在寻找单线并且只想联接路径的一部分而无需创建新方法或引用新库或构造URI值并将其转换为字符串的任何人,那么...

string urlToImage = String.Join("/", "websiteUrl", "folder1", "folder2", "folder3", "item");

这很基本,但是我看不到您还需要什么。如果您害怕将'/'翻倍,则可以简单地进行.Replace("//", "/")事后处理。如果您担心在'https://'中替换双倍的'//',则改为一次联接,替换双倍的'/',然后加入网站网址(但是我敢肯定,大多数浏览器会自动将其前面带有'https:'的内容转换为正确的格式)。看起来像:

string urlToImage = String.Join("/","websiteUrl", String.Join("/", "folder1", "folder2", "folder3", "item").Replace("//","/"));

这里有很多可以解决上述所有问题的答案,但就我而言,我只需要在一个位置一次就可以了,不需要过多地依赖它。另外,很容易看到这里发生了什么。

请参阅:https : //docs.microsoft.com/zh-cn/dotnet/api/system.string.join?view=netframework-4.8


1

采用:

    private Uri UriCombine(string path1, string path2, string path3 = "", string path4 = "")
    {
        string path = System.IO.Path.Combine(path1, path2.TrimStart('\\', '/'), path3.TrimStart('\\', '/'), path4.TrimStart('\\', '/'));
        string url = path.Replace('\\','/');
        return new Uri(url);
    }

它具有行为完全一样的好处Path.Combine


1

这是我的方法,我也将自己使用它:

public static string UrlCombine(string part1, string part2)
{
    string newPart1 = string.Empty;
    string newPart2 = string.Empty;
    string seperator = "/";

    // If either part1 or part 2 is empty,
    // we don't need to combine with seperator
    if (string.IsNullOrEmpty(part1) || string.IsNullOrEmpty(part2))
    {
        seperator = string.Empty;
    }

    // If part1 is not empty,
    // remove '/' at last
    if (!string.IsNullOrEmpty(part1))
    {
        newPart1 = part1.TrimEnd('/');
    }

    // If part2 is not empty,
    // remove '/' at first
    if (!string.IsNullOrEmpty(part2))
    {
        newPart2 = part2.TrimStart('/');
    }

    // Now finally combine
    return string.Format("{0}{1}{2}", newPart1, seperator, newPart2);
}

这仅适用于您的情况。在某些情况下可能会破坏您的代码。另外,您没有对路径的各个部分进行正确的编码。当涉及跨站点脚本攻击时,这可能是一个巨大的漏洞。
Believe2014 2014年

我同意你的观点。该代码应该只是两个URL部分的简单组合。
阿米特·巴加特

1

用这个:

public static class WebPath
{
    public static string Combine(params string[] args)
    {
        var prefixAdjusted = args.Select(x => x.StartsWith("/") && !x.StartsWith("http") ? x.Substring(1) : x);
        return string.Join("/", prefixAdjusted);
    }
}

与“ WebPath”的搭配很好。:)虽然代码可能不必要地密集-我很难一眼就说,是的,这很完美。这让我想看单元测试。也许就是我!
Brian MacKay 2012年

1
x.StartsWith(“ /”)&&!x.StartsWith(“ http”)-为什么要进行http检查?你得到什么?
penguat 2012年

如果不想以http开头的斜线,则不想删除。
Martin Murphy

@BrianMacKay,我不确定两个衬管是否值得进行单元测试,但是如果您愿意的话,可以提供一个。好像我不接受补丁或其他任何东西,但可以随时编辑建议。
Martin Murphy

1

我发现Uri构造函数将“ \”翻转为“ /”。因此,您也可以将Path.Combine,与Uri构造函数一起使用。

 Uri baseUri = new Uri("http://MyUrl.com");
 string path = Path.Combine("Images", "Image.jpg");
 Uri myUri = new Uri(baseUri, path);

1

对于它的价值,这里有一些扩展方法。第一个将合并路径,第二个将参数添加到URL。

    public static string CombineUrl(this string root, string path, params string[] paths)
    {
        if (string.IsNullOrWhiteSpace(path))
        {
            return root;
        }

        Uri baseUri = new Uri(root);
        Uri combinedPaths = new Uri(baseUri, path);

        foreach (string extendedPath in paths)
        {
           combinedPaths = new Uri(combinedPaths, extendedPath);
        }

        return combinedPaths.AbsoluteUri;
    }

    public static string AddUrlParams(this string url, Dictionary<string, string> parameters)
    {
        if (parameters == null || !parameters.Keys.Any())
        {
            return url;
        }

        var tempUrl = new StringBuilder($"{url}?");
        int count = 0;

        foreach (KeyValuePair<string, string> parameter in parameters)
        {
            if (count > 0)
            {
                tempUrl.Append("&");
            }

            tempUrl.Append($"{WebUtility.UrlEncode(parameter.Key)}={WebUtility.UrlEncode(parameter.Value)}");
            count++;
        }

        return tempUrl.ToString();
    }

1

在其他的答案作为发现,无论是新的Uri()还是TryCreate()可以做刻度。但是,基本的Uri必须以结尾,/亲戚的必须不要以/; 开头。否则,它将删除基本网址的结尾部分

我认为最好将其作为扩展方法来完成,即

public static Uri Append(this Uri uri, string relativePath)
{
    var baseUri = uri.AbsoluteUri.EndsWith('/') ? uri : new Uri(uri.AbsoluteUri + '/');
    var relative = relativePath.StartsWith('/') ? relativePath.Substring(1) : relativePath;
    return new Uri(baseUri, relative);
}

并使用它:

var baseUri = new Uri("http://test.com/test/");
var combinedUri =  baseUri.Append("/Do/Something");

在性能方面,由于Uri类进行了大量的解析和验证,因此消耗的资源超过了需要的资源。一个非常粗糙的分析(调试)在大约2秒钟内完成了100万次操作。这将适用于大多数情况,但是要提高效率,最好将所有内容作为字符串进行处理,这需要125毫秒来执行一百万次操作。即

public static string Append(this Uri uri, string relativePath)
{
    //avoid the use of Uri as it's not needed, and adds a bit of overhead.
    var absoluteUri = uri.AbsoluteUri; //a calculated property, better cache it
    var baseUri = absoluteUri.EndsWith('/') ? absoluteUri : absoluteUri + '/';
    var relative = relativePath.StartsWith('/') ? relativePath.Substring(1) : relativePath;
    return baseUri + relative;
}

而且,如果您仍然想返回URI,则一百万次操作大约需要600毫秒。

public static Uri AppendUri(this Uri uri, string relativePath)
{
    //avoid the use of Uri as it's not needed, and adds a bit of overhead.
    var absoluteUri = uri.AbsoluteUri; //a calculated property, better cache it
    var baseUri = absoluteUri.EndsWith('/') ? absoluteUri : absoluteUri + '/';
    var relative = relativePath.StartsWith('/') ? relativePath.Substring(1) : relativePath;
    return new Uri(baseUri + relative);
}

我希望这有帮助。


1

我认为这应该为您提供更大的灵活性,因为您可以根据需要处理任意多个路径段:

public static string UrlCombine(this string baseUrl, params string[] segments)
=> string.Join("/", new[] { baseUrl.TrimEnd('/') }.Concat(segments.Select(s => s.Trim('/'))));
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.