Path.Combine для URL-адресов?

Path.Combine удобен, но есть ли аналогичная функция в .NET framework для URL?

Я ищу такой синтаксис:

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

который вернется:

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

Flurl включает метод Url.Combine, который делает именно это.
Todd Menier 21.02.2014 10:18

Фактически, // обрабатывается маршрутизацией веб-сайта или сервера, а не браузером. Он отправит то, что вы поместили в адресную строку. Вот почему у нас возникают проблемы, когда мы набираем htp: // вместо http: //. Таким образом, // может вызвать серьезные проблемы на некоторых сайтах. Я пишу .dll для сканера, который обрабатывает конкретный веб-сайт, который выдает 404, если у вас есть // в URL-адресе.

Dave Gordon 07.07.2014 12:11
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1 322
2
353 909
40
Перейти к ответу Данный вопрос помечен как решенный

Ответы 40

Вы используете 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

+1: Это хорошо, хотя у меня иррациональная проблема с выходным параметром. ;)

Brian MacKay 29.10.2009 17:24

Это гораздо лучший подход, так как он также будет работать для путей в форме "../../something.html"

wsanville 28.03.2010 04:32

@Brian: если это помогает, все методы TryXXX (int.TryParse, DateTime.TryParseExact) имеют этот выходной параметр, чтобы упростить их использование в операторе if. Кстати, вам не нужно инициализировать переменную, как это сделал Райан в этом примере.

Abel 27.08.2010 02:11

Этот ответ страдает той же проблемой, что и Джоэла: объединение test.com/mydirectory/ и /helloworld.aspx приведет к test.com/helloworld.aspx, что, по-видимому, не то, что вы хотите.

Matt Kocaj 27.08.2013 06:05

Привет, это не удалось для следующего: if (Uri.TryCreate (new Uri ("localhost / MyService / ")," / Event / SomeMethod? Abc = 123 ", out result)) {Console.WriteLine (result);} Он показывает мне результат как : localhost / Событие / SomeMethod? abc = 123 Примечание: "http: //" заменяется здесь базовым Uri на stackoverflow

Faisal Mq 28.11.2013 12:45

@FaisalMq Это правильное поведение, поскольку вы передали второй параметр, относящийся к корню. Если бы вы не указали начальную / во втором параметре, вы бы получили ожидаемый результат.

Tom Lint 24.01.2014 13:17

Та же проблема, что и в ответе вершина: Uri.TryCreate(new Uri("http://test.com/mydirectory"), "helloworld.aspx", out result) установит результат на http://test.com/helloworld.aspx

Lu55 16.09.2016 20:12

@ Lu55, если вы поместите / после каталога, он будет работать.

Florian Fida 27.01.2017 04:51

Чтобы это работало, базовый Uri должен иметь завершающую косую черту, а относительный Uri НЕ должен иметь ведущую косую черту. т.е. База: «test.com/test» Относительный: «do / did / done» результат: «test.com/test/do/did/done» Это также эквивалентно u = new Uri (new Uri («test.com/test/ "),« / do / did / done »);

Mahmoud Hanafy 08.11.2019 00:48

Остроумный пример, Райан, заканчивающийся ссылкой на функцию. Отличная работа.

Одна рекомендация, Брайан: если вы заключите этот код в функцию, вы можете использовать UriBuilder для обертывания базового URL-адреса перед вызовом TryCreate.

В противном случае базовый 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");
}

Это может быть достаточно простое решение:

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

+1: Хотя это не обрабатывает пути относительного стиля (../../whatever.html), мне нравится этот путь за его простоту. Я бы также добавил обрезки для символа '\'.

Brian MacKay 08.05.2010 19:55

См. Мой ответ для более полной версии этого.

Brian MacKay 12.05.2010 17:46

@BrianMacKay, OP никогда не запрашивал пути относительного стиля ...

Mladen B. 22.03.2021 18:49

@MladenB. Ну, я ОП. :) Хотя я явно не просил об этом, необходимость поддержки путей относительного стиля является неотъемлемой частью всеобъемлющей проблемной области ... Невыполнение этого может привести к запутанным результатам, если люди попытаются использовать это повторно.

Brian MacKay 23.03.2021 17:19

лол, не осознавал этого :) извините :)

Mladen B. 24.03.2021 02:56

У Uri есть конструктор, который сделает это за вас: new Uri(Uri baseUri, string relativeUri)

Вот пример:

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

Примечание редактора: будьте осторожны, этот метод не работает должным образом. В некоторых случаях он может вырезать часть baseUri. Смотрите комментарии и другие ответы.

Как фанат использования как можно большего количества уже созданного кода, мне было интересно, почему никто еще не предложил этого, пока я не заметил ваш ответ. ИМО, это лучший ответ.

Chris 28.03.2010 04:30

Мне нравится использование класса Uri, к сожалению, он не будет вести себя как Path.Combine, как просил OP. Например, новый Uri (новый Uri ("test.com/mydirectory/ ")," /helloworld.aspx "). ToString () дает вам" test.com/helloworld.aspx "; что было бы неверно, если бы мы хотели получить результат в стиле Path.Combine.

Doctor Jones 28.10.2010 19:20

Все дело в косых чертах. Если часть относительного пути начинается с косой черты, она ведет себя так, как вы описали. Но если убрать косую черту, все будет работать так, как вы и ожидали (обратите внимание на отсутствующую косую черту во втором параметре): new Uri (new Uri ("test.com/mydirectory/ ")," helloworld.aspx "). ToString () results в "test.com/mydirectory/helloworld.aspx". Path.Combine ведет себя аналогичным образом. Если параметр относительного пути начинается с косой черты, он возвращает только относительный путь и не объединяет их.

Joel Beckham 29.10.2010 02:11

Если ваш baseUri оказался «test.com/mydirectory/mysubdirectory», то результатом будет «test.com/mydirectory/helloworld.aspx» вместо «test.com/mydirectory/mysubdirectory/helloworld.aspx». Тонкое отличие - отсутствие завершающей косой черты в первом параметре. Я за то, чтобы использовать существующие методы фреймворка, если у меня уже должна быть конечная косая черта, тогда я думаю, что выполнение partUrl1 + partUrl2 пахнет намного меньше - я мог бы довольно долго гоняться за этой конечной косой чертой. ради того, чтобы не выполнять конкатенацию строк.

Carl 12.01.2011 19:10

Единственная причина, по которой мне нужен метод комбинирования URI, - это то, что мне не нужно проверять завершающую косую черту. Request.ApplicationPath - это '/', если ваше приложение находится в корне, и '/ foo', если это не так.

nickd 25.03.2011 19:44

Я -1 этот ответ, потому что это не решает проблему. Когда вы хотите объединить URL-адреса, например, когда вы хотите использовать Path.Combine, вам не нужно заботиться о завершающем /. и об этом нужно заботиться. Я предпочитаю решение Брайана Маккея или mdsharpe выше

Baptiste Pernet 21.04.2011 19:56

@Baptiste Pernet: Хороший момент - в целом вы правы. В конкретном случае OP, когда baseUri не имеет дополнительных элементов пути, конечная косая черта не влияет на результат. Если в baseUri есть дополнительные элементы пути, например "Http://MyUrl.com/some/folder", значит, вы правы, вам нужно обратить внимание, чтобы там оставалась косая черта в конце.

Joel Beckham 11.05.2011 00:05

Этот код работает неправильно, если вы развертываете приложение по виртуальному пути, а не в корне веб-сайта. Если ваш базовый URL будет my.website.com/virtualdir, а относительный путь будет catalog / page.html, это вернет my.website.com/catalog/page.html, vitrualdir пропущен

Bogdan_Ch 17.07.2013 16:24

Раньше я был большим поклонником этого подхода и использовал его повсюду, а затем меня сильно укусило, когда я узнал о неприятной привычке класса Uri к параметрам строки запроса декодирования URL. Хуже того, это задумано. См. stackoverflow.com/a/7307950. Как бы я ни ненавидел это, теперь я выполняю все конкатенации URL вручную. По крайней мере, это не испортит мою реплику.

Daniel Liuzzi 25.03.2014 00:51

Этот ответ не доставляет товары, если первый URI не является корнем домена. Стыд.

Martin Capodici 14.05.2014 09:28

Потрясающие! он даже работает в том, что Uri baseUri = new Uri("http://www.foo.com/lol"); Uri myUri = new Uri(baseUri, "../test"); и myUri.ToString() дают http://www.foo.com/test

Jack 18.12.2014 22:43

Слэши или нет, похоже, это не работает для URL-адресов, таких как var baseuri = new Uri("http://dotnettfs:8080/tfs/softwarecollection");var myuri = new Uri(baseuri, "_versionControl/changeset/244603");//myuri = http://dotnettfs:8080/tfs/_versionControl/changeset/244603

Joshua C 21.08.2015 02:23

-1 new Uri(new Uri("test.com/mydirectory"), "helloworld.aspx") возвращает test.com/helloworld.aspx, чего никому не нужно.

BlueRaja - Danny Pflughoeft 11.10.2015 00:57

@DoctorJones Это правильное поведение. Path.Combine примет любой параметр с ведущим \ в качестве нового корня, перекрывая любой ранее указанный корень. Так что конструктор Uri действительно поступает правильно. Эта «особенность» Path.Combine уже несколько раз укусила меня в прошлом.

Tom Lint 11.11.2015 13:18

@TomLint Я понимаю, что это так задумано. Я утверждал, что он ведет себя иначе, чем Path.Combine, потому что OP запросил эквивалент Path.Combine для URL-адресов. Конструктор Uri не эквивалент Path.Combine.

Doctor Jones 11.11.2015 14:33

@DoctorJones Я не думаю, что вы полностью поняли, что я сказал; Path.Combine имеет точно такое же поведение в отношении параметров, начинающихся с символа разделителя пути. Поэтому объединение Uris таким образом является правильно, и именно то, что просил OP.

Tom Lint 11.11.2015 16:13

Это не ведет себя так, как просили операторы

Reda 20.01.2016 12:39

По сути, это не работает, если базовый путь является относительным и не содержит всей необходимой информации, чтобы сделать его абсолютным URI. Итак, комбинируя /a/b/c/ с d/e/f с выбросом ArgumentOutOfRangeException. Совершенно непригоден для этой цели.

LB2 24.05.2016 00:24

URL-адреса - это абстракции, которые МОГУТ переводиться в путь файловой системы, но не следует думать об этом. Это никогда не сможет работать как Path.Combine без некоторых серьезных предположений. Если у вас есть URL-адрес: blah.com/thing1/thing2, чей сказать, что thing2 - это каталог или файл? Если вы попытаетесь объединить это с относительным путем "../thing3", должно ли оно разрешиться на blah.com/thing1/thing3 или blah.com/thing3? Теперь thing3 - это каталог или файл? Без явного добавления завершающих слэшей ВРУЧНУЮ или какого-либо другого настраиваемого правила синтаксического анализа это не сработает. Большинство веб-серверов не раскрывают эту информацию.

Adam Plocher 25.04.2017 08:37

Это решение генерирует предупреждение: docs.microsoft.com/de-de/visualstudio/code-quality/…

Simon 14.03.2019 11:04

Если у нас есть такой url, результат будет неверным Пример: 127.0.0.1:8080/drupal

mohammad almasi 14.12.2019 16:51

Считаю это правильным ответом. URI не может ссылаться на каталог. Он всегда относится к ресурсу, который является последней заданной частью, если он не заканчивается на /, и в этом случае он относится к ресурсу по умолчанию в этом пути. Следовательно, «локальный / каталог / подкаталог» является ложным предположением, потому что «подкаталог» на самом деле является ресурсом (файлом think), а не набором ресурсов. Именно так работают URI, и комбинирование этого с относительным путем всегда должно удалять часть «подкаталог». И это именно то, что делает конструктор Uri.

Robert McKee 14.08.2020 22:02

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

Основываясь на этом предположении, я предлагаю это решение как наиболее подходящий ответ на ваш вопрос: «Path.Combine удобен, есть ли аналогичная функция в структуре для URL-адресов?»

Поскольку в структуре для URL-адресов есть аналогичная функция, я предлагаю правильный метод: "VirtualPathUtility.Combine". Вот справочная ссылка MSDN: VirtualPathUtility.Combine - метод

Есть одно предостережение: я считаю, что это работает только для URL-адресов, относящихся к вашему сайту (то есть вы не можете использовать его для создания ссылок на другой веб-сайт. Например, var url = VirtualPathUtility.Combine("www.google.com", "accounts/widgets");).

+1, потому что это близко к тому, что я ищу, хотя было бы идеально, если бы это работало для любого старого URL-адреса. Я удвоил это, и он станет намного элегантнее, чем то, что предлагал mdsharpe.

Brian MacKay 30.03.2010 01:28

Предупреждение правильное, он не может работать с абсолютным uris, и результат всегда является относительным от корня. Но у него есть дополнительное преимущество, он обрабатывает тильду, как и "~ /". Это делает его ярлыком для Server.MapPath и комбинирования.

Abel 27.08.2010 02:18

Ответ Райана Кука близок к тому, что мне нужно, и может быть более подходящим для других разработчиков. Однако он добавляет 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

Кстати о деталях: как насчет обязательного ArgumentNullException("url1"), если аргумент - Nothing? Извините, просто привередничать ;-). Обратите внимание, что обратная косая черта не имеет ничего общего с URI (и если она есть, ее не следует обрезать), поэтому вы можете удалить ее из TrimXXX.

Abel 27.08.2010 02:21

вы можете использовать params string [] и рекурсивно объединить их, чтобы разрешить более 2 комбинаций

Jaider 13.06.2012 02:47

Это хорошее решение, но оно не касается следующих случаев: Assert.IsTrue(target.UrlCombine("../test1", "/") = "../test1/") Assert.IsTrue(target.UrlCombine("/", "test1/") = "/test1/")

Vlax 31.01.2013 15:20

Я понимаю, что вы имеете в виду по поводу тестов. Поскольку код на самом деле не работает с точки зрения разделения вещей на каталоги и последующего склеивания их вместе, а вместо этого просто использует текст, я даже не подумал добавить их. Они пройдут, поскольку они в основном покрыты ... Вы можете свободно редактировать код, если хотите.

Brian MacKay 31.01.2013 21:33

Извините, я был неправ, ваш код действительно охватывает упомянутый случай!

Vlax 08.02.2013 16:35

@LouisRhys Так бывает, потому что в то время я работал над проектом VB.net. ;) Но я обновил это переводом на C#.

Brian MacKay 04.05.2014 15:28

Я бы очень хотел, чтобы это было в библиотеке базовых классов, например Path.Combine.

Uriah Blatherwick 18.08.2014 22:30

@MarkHurd, я согласен. Я не понимал, что это Char (), я думал, что это был единственный Char. Имеет смысл, почему они так поступили. Я бы предпочел New Char() { "/"c, "\"c }

JJS 07.07.2016 22:21

@MarkHurd Я снова отредактировал код, так что он поведенчески такой же, как C#, а также синтаксически эквивалентен.

JJS 07.07.2016 22:23

@JJS Похоже, кто-то сломал это, а вы вернулись и исправили это - хорошая работа. :)

Brian MacKay 11.07.2016 16:21

@BrianMacKay я сломал его, markhurd указал на мою ошибку и откатился, я снова обновился ... ура

JJS 11.07.2016 16:28

Простой способ объединить их и убедиться, что он всегда правильный:

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

+1, хотя это очень похоже на ответ mdsharpe, который я улучшил в своем ответе. Эта версия отлично работает, если только Url2 не начинается с / или \, или Url1 случайно не заканчивается на \, или один из них не пуст! :)

Brian MacKay 13.05.2010 03:57

Я должен отметить, что Path.Combine, похоже, работает и для этого напрямую, по крайней мере, на .NET 4.

Если вы используете Path.Combine, у вас будет что-то вроде этого: www.site.com/foo\wrong\icon.png

Lehto 25.10.2010 17:56

Точно. Я потратил некоторое время на реализацию функции Uri.Combine именно по этой причине: stackoverflow.com/a/23399048/3481183

Believe2014 01.05.2014 19:37

Я просто собрал небольшой метод расширения:

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");
Path.Combine("Http://MyUrl.com/", "/Images/Image.jpg").Replace("\\", "/")
path.Replace(Path.DirectorySeparatorChar, '/');
Jaider 13.06.2012 02:51
path.Replace(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar)
SliverNinja - MSFT 16.08.2012 18:31

Чтобы заставить его работать, вы должны удалить первый / второй аргумент, т.е. «/ Images» - / Path.Combine («Http://MyUrl.com», «Images / Image.jpg»)

Per G 25.04.2013 14:43

@SliverNinja Это неверно Значением этого поля является обратная косая черта ('\') в UNIX и косая черта ('/') в операционных системах Windows и Macintosh. При использовании Mono в системе Linux вы получите неправильный разделитель.

user247702 13.03.2014 14:01

Удивительно, что ни один из других массовых сторонников не упомянул, что другие решения работают только для каталогов / имен два.

mike 05.04.2016 02:05

Все вы, придумавшие, что такое разделитель каталогов, забывают, что строки могли быть получены из другой ОС, нежели вы используете сейчас. Просто замените обратную косую черту на косую черту, и все готово.

JeremyWeir 27.07.2016 01:43

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: Сейчас мы говорим ... Я попробую. Это может даже стать новым принятым ответом. После попытки использовать новый метод Uri () мне он действительно не понравился. Слишком привередливы.

Brian MacKay 18.07.2011 18:23

Это именно то, что мне было нужно! Не был поклонником необходимости заботиться о том, где ставить конечные слэши и т. д.

Gromer 07.08.2012 01:52

+1 за откат нулевой проверки, чтобы он не взорвался.

NightOwl888 06.03.2014 17:34

Count () должен иметь значение Length, чтобы вам не нужно было включать Linq в свою библиотеку только для этого.

PRMan 06.06.2019 00:19

Это было именно то, что я искал.

ThePeter 27.04.2020 18:04

Здесь уже есть отличные ответы. Основываясь на предложении 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

Это решение упрощает написание статического метода UriUtils.Combine («базовый URL», «часть1», «часть2», ...), который очень похож на Path.Combine (). Хороший!

angularsen 19.11.2011 19:23

Для поддержки относительных URI мне пришлось использовать ToString () вместо AbsoluteUri и UriKind.AbsoluteOrRelative в конструкторе Uri.

angularsen 20.11.2011 23:34

Спасибо за подсказку о родственнике Юрисе. К сожалению, Uri не упрощает работу с относительными путями, поскольку всегда есть некоторая путаница с задействованным Request.ApplicationPath. Возможно, вы также могли бы попробовать использовать новый Uri (HttpContext.Current.Request.ApplicationPath) в качестве основы и просто вызвать для него Append? Это даст вам абсолютные пути, но должно работать где угодно в структуре сайта.

Ales Potocnik Hahonina 21.11.2011 16:03

Я также добавил проверку, не является ли какой-либо из добавляемых путей пустой или пустой строкой.

n.podbielski 15.01.2016 13:13

Когда я просматривал все ответы, я подумал ... «Почему еще никто не опубликовал метод расширения, я собираюсь опубликовать его» ... Неважно. +1

Arvo Bowen 06.02.2021 01:00

Использовать это:

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 11.12.2012 01:42

x.StartsWith ("/") &&! x.StartsWith ("http") - почему проверка http? что вы получаете?

penguat 13.12.2012 14:03

Не пытайтесь убрать косую черту, если она начинается с http.

Martin Murphy 16.04.2013 20:12

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

Martin Murphy 16.04.2013 20:23

Я еще не использовал следующий код, но во время своих путешествий по Интернету нашел его для решения проблемы объединения URL-адресов - надеюсь, что это краткий (и успешный!) Ответ:

VirtualPathUtility.Combine

На самом деле не очень полезно. Есть несколько обращений Google, объясняющих некоторые из его проблем, но, помимо того, что ему не нравится "http: // ..." в начале, он фактически удаляет последний подпуть первого аргумента, если он не заканчивается на a "/"! Хотя описание MSDN звучит хорошо!

Mark Hurd 02.03.2013 11:21

Я объяснил и дал решение этой проблемы в своем ответе stackoverflow.com/a/23399048/3481183

Believe2014 01.05.2014 19:36

Я объединил все предыдущие ответы:

    public static string UrlPathCombine(string path1, string path2)
    {
        path1 = path1.TrimEnd('/') + "/";
        path2 = path2.TrimStart('/');

        return Path.Combine(path1, path2)
            .Replace(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);
    }

    [TestMethod]
    public void TestUrl()
    {
        const string P1 = "http://msdn.microsoft.com/slash/library//";
        Assert.AreEqual("http://msdn.microsoft.com/slash/library/site.aspx", UrlPathCombine(P1, "//site.aspx"));

        var path = UrlPathCombine("Http://MyUrl.com/", "Images/Image.jpg");

        Assert.AreEqual(
            "Http://MyUrl.com/Images/Image.jpg",
            path);
    }

Вы могли бы использовать класс VirtualPathUtiliy для безопасного добавления и удаления завершающих слэшей. Посмотрите мой ответ: stackoverflow.com/a/23399048/3481183

Believe2014 01.05.2014 19:38

Я обнаружил, что UriBuilder действительно хорошо подходит для таких вещей:

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

См. Класс UriBuilder - MSDN для получения дополнительных конструкторов и документации.

Вот мой подход, и я буду использовать его для себя:

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 01.05.2014 19:40

Я согласен с вашими пунктами. Код должен выполнять простое объединение двух частей URL.

Amit Bhagat 02.05.2014 07:14

Использовать:

    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.

Объединение нескольких частей URL-адреса может быть немного сложным. Вы можете использовать двухпараметрический конструктор 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

У меня есть модульные тесты для трех наиболее распространенных случаев:

Enter image description here

+1 за все дополнительные усилия. Мне нужно немного поддержать этот вопрос для некоторых из наиболее высоко оцененных ответов, вы бросили перчатку. ;)

Brian MacKay 04.05.2014 15:42

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

    public class UriTool
    {
        public static Uri Join(string path1, string path2)
        {
            string url = path1 + "/" + path2;
            url = Regex.Replace(url, "(?<!http:)/{2,}", "/");

            return new Uri(url);
        }
    }

Итак, вы можете использовать это так:

    string path1 = "http://someaddress.com/something/";
    string path2 = "/another/address.html";
    Uri joinedUri = UriTool.Join(path1, path2);

    // joinedUri.ToString() returns "http://someaddress.com/something/another/address.html"

Мое общее решение:

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;
}

Этот вспомогательный метод очень гибкий и хорошо работает во многих различных случаях использования. Спасибо!

Shiva 22.05.2017 08:11

Вот метод UrlUtility.Combine от Microsoft (OfficeDev PnP):

    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 02.11.2015 21:12

@BrianMacKay Согласен, что это похоже, но это из класса UrlUtility и используется в контексте объединения URL-адресов.

user3638471 03.11.2015 22:55

Отредактировано, чтобы уточнить, к какому классу он принадлежит

user3638471 03.11.2015 22:57

Будьте осторожны при использовании этого класса, остальная часть класса содержит артефакты, специфичные для SharePoint.

Harry Berry 26.09.2017 15:55

Я использовал этот код для решения проблемы:

string[] brokenBaseUrl = Context.Url.TrimEnd('/').Split('/');
string[] brokenRootFolderPath = RootFolderPath.Split('/');

for (int x = 0; x < brokenRootFolderPath.Length; x++)
{
    //if url doesn't already contain member, append it to the end of the string with / in front
    if (!brokenBaseUrl.Contains(brokenRootFolderPath[x]))
    {
        if (x == 0)
        {
            RootLocationUrl = Context.Url.TrimEnd('/');
        }
        else
        {
            RootLocationUrl += String.Format("/{0}", brokenRootFolderPath[x]);
        }
    }
}

Правила при объединении URL-адресов с URI

Чтобы избежать странного поведения, нужно следовать одному правилу:

  • Путь (каталог) должен заканчиваться на '/'. Если путь заканчивается без '/', последняя часть рассматривается как имя файла и будет объединена при попытке объединения со следующей частью URL-адреса.
  • Есть одно исключение: базовый URL-адрес (без информации о каталоге) не должен заканчиваться на '/'.
  • часть пути не должна начинаться с символа "/". Если он начинается с '/', вся существующая относительная информация из URL-адреса удаляется ... добавление пути к части string.Empty также удалит относительный каталог из 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();
        });

Я создал эту функцию, которая облегчит вам жизнь:

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

Я считаю следующее полезным и имеет следующие особенности:

  • Выбрасывает нулевое или пустое пространство
  • Принимает несколько параметров params для нескольких сегментов URL
  • выбрасывает ноль или пустой

Класс

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

Некоторые проблемы с тестами: // Result = test1 / test2 / test3 \ для четвертого и последнего из тестов throws дает ArgumentNullException вместо ArgumentException

Moriya 22.01.2020 12:44

Оба из них работают:

  Uri final = new Uri(Regex.Replace(baseUrl + "/" + relativePath, "(?<!http:)/{2,}", "/"));

Или же

  Uri final =new Uri(string.Format("{0}/{1}", baseUrl.ToString().TrimEnd('/'), relativePath.ToString().TrimStart('/')));

Т.е. если

baseUrl = "http://tesrurl.test.com/Int18"

а также

relativePath = "To_Folder"

output = http://tesrurl.test.com/Int18/To_Folder

Некоторые ошибки появятся в приведенном ниже коде:

 // If you use the below code, some issues will be there in the final URI
 Uri final = new Uri(baseUrl, relativePath);

Простой однострочник:

public static string Combine(this string uri1, string uri2) => $"{uri1.TrimEnd('/')}/{uri2.TrimStart('/')}";

Вдохновленный ответом @Matt Sharpe.

Почему бы просто не использовать следующее.

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

Underverse 06.02.2018 03:14

Хорошая идея, но она терпит неудачу, когда один из параметров равен нулю.

pholpar 22.03.2018 17:19

Мы используем следующий простой вспомогательный метод, чтобы объединить произвольное количество частей URL:

public static string JoinUrlParts(params string[] urlParts)
{
    return string.Join("/", urlParts.Where(up => !string.IsNullOrEmpty(up)).ToList().Select(up => up.Trim('/')).ToArray());
}

Обратите внимание, что он не поддерживает относительные URL-адреса в стиле '../../something/page.htm'!

Ответ принят как подходящий

Есть комментарий Тодда Меньера выше, что Flurl включает в себя Url.Combine.

Подробнее:

Url.Combine is basically a Path.Combine for URLs, ensuring one and only one separator character between parts:

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" 

Получите Flurl.Http в NuGet:

PM> Установить пакет Flurl.Http

Или получить автономный конструктор URL без функций HTTP:

PM> Установка пакета Flurl

Что ж, этот вопрос получает много трафика, и ответ с более чем 1000 положительными голосами на самом деле работает не во всех случаях. Спустя годы я фактически использую Flurl для этого, поэтому я принимаю его. Кажется, это работает во всех случаях, с которыми я сталкивался. Если люди не хотят принимать зависимость, я опубликовал ответ, который также отлично работает.

Brian MacKay 28.11.2018 18:33

и если вы не используете Flurl и предпочитаете облегченную версию, github.com/jean-lourenco/UrlCombine

lizzy91 01.02.2019 22:59

Я обнаружил, что конструктор Uri переворачивает '\' в '/'. Таким образом, вы также можете использовать Path.Combine с конструктором Uri.

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

Как бы то ни было, вот пара методов расширения. Первый объединяет пути, а второй добавляет параметры к 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();
    }

Если вы не хотите добавлять стороннюю зависимость, такую ​​как Flurl, или создавать собственный метод расширения в ASP.NET Core (также доступном в Microsoft.Owin), вы можете использовать PathString, который предназначен для создания Пути URI. Затем вы можете создать свой полный URI, используя комбинацию этого, Uri и UriBuilder.

В этом случае это будет:

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

Это дает вам все составные части без необходимости указывать разделители в базовом URL. К сожалению, PathString требует, чтобы к каждой строке добавлялся /, иначе он фактически выбрасывает ArgumentException! Но, по крайней мере, вы можете детерминированно построить свой URI таким образом, чтобы его было легко тестировать по модулю.

Итак, у меня есть другой подход, похожий на всех, кто использовал UriBuilder.

Я не хотел разделять свой BaseUrl (который может содержать часть пути - например, http://mybaseurl.com/dev/), как это сделал javajavajavajavajava.

В следующем фрагменте кода показан код + Tests.

Остерегаться: Это решение уменьшает регистр хоста и добавляет порт. Если это нежелательно, можно записать строковое представление, например, используя свойство UriUriBuilder.

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

Протестировано с .NET Core 2.1 в Windows 10.

Почему это работает?

Несмотря на то, что Path.Combine вернет обратную косую черту (по крайней мере, в Windows), UriBuilder обрабатывает этот случай в установщике Path.

Взято из 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;
      }
 }

Это лучший подход?

Конечно, это решение довольно самоописуемое (по крайней мере, на мой взгляд). Но вы полагаетесь на недокументированную (по крайней мере, я ничего не нашел с помощью быстрого поиска в Google) «функцию» из .NET API. Это может измениться в будущем выпуске, поэтому, пожалуйста, рассмотрите метод с тестами.

В https://github.com/dotnet/corefx/blob/master/src/System.Private.Uri/tests/FunctionalTests/UriBuilderTests.cs (Path_Get_Set) есть тесты, которые проверяют, правильно ли преобразован \.

Примечание: Можно также работать со свойством UriBuilder.Uri напрямую, если uri будет использоваться для ctor System.Uri.

Это очень надежный подход. Поднимите палец вверх за модульный тест !!

aggsol 02.10.2019 09:59

Для тех, кто ищет однострочник и просто хочет соединить части пути без создания нового метода или ссылки на новую библиотеку или создать значение URI и преобразовать его в строку, затем ...

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

Это довольно просто, но я не понимаю, что вам еще нужно. Если вы боитесь удвоения '/', тогда вы можете просто сделать .Replace("//", "/") после этого. Если вы боитесь заменить удвоенный «//» в «https: //», то вместо этого сделайте одно соединение, замените удвоенный «/», а затем присоединитесь к URL-адресу веб-сайта (однако я уверен, что большинство браузеров автоматически преобразуйте что-либо с https: перед ним, чтобы читать в правильном формате). Это выглядело бы так:

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

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

См .: https://docs.microsoft.com/en-us/dotnet/api/system.string.join?view=netframework-4.8

Как указано в других ответах, новый Uri() или TryCreate() могут подойти. Однако базовый Uri должен заканчиваться на /, а родственник НЕ должен начинаться на /; в противном случае будет удалена конечная часть базового URL-адреса.

Я думаю, что это лучше всего сделать как метод расширения, т.е.

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, который выполняет большой анализ и проверку; очень грубое профилирование (Debug) выполнило миллион операций примерно за 2 секунды. Это будет работать для большинства сценариев, однако для большей эффективности лучше управлять всем как строками, это занимает 125 миллисекунд для 1 миллиона операций. Т.е.

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 миллисекунд для 1 миллиона операций.

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

Надеюсь, это поможет.

Недавно в пакет Energy.Core был добавлен метод Комбинировать, поэтому вы можете использовать его для соединения частей URL.

    string url;
    url = Energy.Base.Url.Combine("https://www.youtube.com", "watch?v=NHCgbs3TcYg");
    Console.WriteLine(url);
    url = Energy.Base.Url.Combine("https://www.youtube.com", "watch?v=NHCgbs3TcYg", "t=150");
    Console.WriteLine(url);

Кроме того, он распознает часть параметра, поэтому он будет работать так, как вы могли ожидать (объединение пути с косой чертой и параметров с амперсандом).

https://thewikihow.com/video_NHCgbs3TcYg

https://thewikihow.com/video_NHCgbs3TcYg&t=150

Документация по классу Energy.Base.Url

Пакет в галерее NuGet

Образец кода

Я думаю, это должно дать вам больше гибкости, так как вы можете работать с любым количеством сегментов пути:

public static string UrlCombine(this string baseUrl, params string[] segments)
=> string.Join("/", new[] { baseUrl.TrimEnd('/') }.Concat(segments.Select(s => s.Trim('/'))));

Если вы не хотите иметь такую ​​зависимость, как Flurl, вы можете использовать его исходный код:

    /// <summary>
    /// Basically a Path.Combine for URLs. Ensures exactly one '/' separates each segment,
    /// and exactly on '&amp;' separates each query parameter.
    /// URL-encodes illegal characters but not reserved characters.
    /// </summary>
    /// <param name = "parts">URL parts to combine.</param>
    public static string Combine(params string[] parts) {
        if (parts == null)
            throw new ArgumentNullException(nameof(parts));

        string result = "";
        bool inQuery = false, inFragment = false;

        string CombineEnsureSingleSeparator(string a, string b, char separator) {
            if (string.IsNullOrEmpty(a)) return b;
            if (string.IsNullOrEmpty(b)) return a;
            return a.TrimEnd(separator) + separator + b.TrimStart(separator);
        }

        foreach (var part in parts) {
            if (string.IsNullOrEmpty(part))
                continue;

            if (result.EndsWith("?") || part.StartsWith("?"))
                result = CombineEnsureSingleSeparator(result, part, '?');
            else if (result.EndsWith("#") || part.StartsWith("#"))
                result = CombineEnsureSingleSeparator(result, part, '#');
            else if (inFragment)
                result += part;
            else if (inQuery)
                result = CombineEnsureSingleSeparator(result, part, '&');
            else
                result = CombineEnsureSingleSeparator(result, part, '/');

            if (part.Contains("#")) {
                inQuery = false;
                inFragment = true;
            }
            else if (!inFragment && part.Contains("?")) {
                inQuery = true;
            }
        }
        return EncodeIllegalCharacters(result);
    }

    /// <summary>
    /// URL-encodes characters in a string that are neither reserved nor unreserved. Avoids encoding reserved characters such as '/' and '?'. Avoids encoding '%' if it begins a %-hex-hex sequence (i.e. avoids double-encoding).
    /// </summary>
    /// <param name = "s">The string to encode.</param>
    /// <param name = "encodeSpaceAsPlus">If true, spaces will be encoded as + signs. Otherwise, they'll be encoded as %20.</param>
    /// <returns>The encoded URL.</returns>
    public static string EncodeIllegalCharacters(string s, bool encodeSpaceAsPlus = false) {
        if (string.IsNullOrEmpty(s))
            return s;

        if (encodeSpaceAsPlus)
            s = s.Replace(" ", "+");

        // Uri.EscapeUriString mostly does what we want - encodes illegal characters only - but it has a quirk
        // in that % isn't illegal if it's the start of a %-encoded sequence https://stackoverflow.com/a/47636037/62600

        // no % characters, so avoid the regex overhead
        if (!s.Contains("%"))
            return Uri.EscapeUriString(s);

        // pick out all %-hex-hex matches and avoid double-encoding 
        return Regex.Replace(s, "(.*?)((%[0-9A-Fa-f]{2})|$)", c => {
            var a = c.Groups[1].Value; // group 1 is a sequence with no %-encoding - encode illegal characters
            var b = c.Groups[2].Value; // group 2 is a valid 3-character %-encoded sequence - leave it alone!
            return Uri.EscapeUriString(a) + b;
        });
    }

Другие вопросы по теме