Path.Combine jest przydatny, ale czy istnieje podobna funkcja w .NET dla adresów URL ?
Szukam takiej składni:
Url.Combine("http://MyUrl.com/", "/Images/Image.jpg")
który zwróciłby:
"http://MyUrl.com/Images/Image.jpg"
Path.Combine jest przydatny, ale czy istnieje podobna funkcja w .NET dla adresów URL ?
Szukam takiej składni:
Url.Combine("http://MyUrl.com/", "/Images/Image.jpg")
który zwróciłby:
"http://MyUrl.com/Images/Image.jpg"
Odpowiedzi:
Istnieje komentarz Todda Meniera powyżej, że Flurl zawiera Url.Combine
.
Więcej szczegółów:
Url.Combine jest w zasadzie Path.Combine dla adresów URL, zapewniając jeden i tylko jeden znak separatora między częściami:
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"
Pobierz Flurl.Http na NuGet :
PM> Zainstaluj pakiet Flurl
Lub uzyskaj autonomiczny program do tworzenia adresów URL bez funkcji HTTP:
PM> Zainstaluj pakiet Flurl
Flurl
i korzystałbyś z lekkiej wersji, github.com/jean-lourenco/UrlCombine
Uri
ma konstruktor, który powinien to zrobić dla Ciebie: new Uri(Uri baseUri, string relativeUri)
Oto przykład:
Uri baseUri = new Uri("http://www.contoso.com");
Uri myUri = new Uri(baseUri, "catalog/shownew.htm");
Uwaga od edytora: Uwaga: ta metoda nie działa zgodnie z oczekiwaniami. W niektórych przypadkach może wyciąć część baseUri. Zobacz komentarze i inne odpowiedzi.
Może to być odpowiednio proste rozwiązanie:
public static string Combine(string uri1, string uri2)
{
uri1 = uri1.TrimEnd('/');
uri2 = uri2.TrimStart('/');
return string.Format("{0}/{1}", uri1, uri2);
}
Używasz 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);
}
Wróci:
http://msdn.microsoft.com/en-us/library/system.uri.trycreate.aspx
int.TryParse
, DateTime.TryParseExact
) mają ten parametr wyjściowy, aby ułatwić korzystanie z nich w instrukcji if. Przy okazji, nie musisz inicjalizować zmiennej, jak Ryan w tym przykładzie.
test.com/mydirectory/
i /helloworld.aspx
spowoduje, że test.com/helloworld.aspx
pozornie nie jest to, czego chcesz.
Tutaj jest już kilka świetnych odpowiedzi. W oparciu o sugestię mdsharpe, oto metoda rozszerzenia, której można łatwo użyć, gdy chcesz poradzić sobie z instancjami 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('/'))));
}
}
I przykład użycia:
var url = new Uri("http://example.com/subpath/").Append("/part1/", "part2").AbsoluteUri;
Spowoduje to utworzenie http://example.com/subpath/part1/part2
Odpowiedź Ryana Cooka jest zbliżona do tego, czego szukam i może być bardziej odpowiednia dla innych programistów. Dodaje jednak http: // na początku łańcucha i generalnie wykonuje nieco więcej formatowania niż ja.
Również w moich przypadkach użycia rozwiązywanie ścieżek względnych nie jest ważne.
Odpowiedź mdsharp zawiera również zalążek dobrego pomysłu, chociaż faktyczna implementacja wymagała jeszcze kilku szczegółów. To jest próba rozwinięcia go (i używam tego w produkcji):
DO#
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
Ten kod przechodzi następujący test, który zdarza się w 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")
jeśli argument jest Nothing
? Przepraszam, jestem wybredna ;-). Zwróć uwagę, że ukośnik odwrotny nie ma nic wspólnego z identyfikatorem URI (a jeśli już istnieje, nie należy go przycinać), więc możesz go usunąć z TrimXXX.
Path.Combine nie działa dla mnie, ponieważ mogą istnieć znaki takie jak „|” w argumentach QueryString, a zatem adres URL, co spowoduje wyjątek ArgumentException.
Najpierw wypróbowałem nowe Uri(Uri baseUri, string relativeUri)
podejście, które nie powiodło się z powodu URI takich jak http://www.mediawiki.org/wiki/Special:SpecialPages
:
new Uri(new Uri("http://www.mediawiki.org/wiki/"), "Special:SpecialPages")
spowoduje utworzenie Special: SpecialPages, ponieważ dwukropek Special
oznacza schemat.
W końcu musiałem wybrać trasę mdsharpe / Brian MacKays i rozwinąłem ją nieco dalej, aby pracować z wieloma częściami 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;
}
Stosowanie: CombineUri("http://www.mediawiki.org/", "wiki", "Special:SpecialPages")
Na podstawie podanego przez Ciebie przykładowego adresu URL założę, że chcesz łączyć adresy URL dotyczące Twojej witryny.
W oparciu o to założenie zaproponuję to rozwiązanie jako najbardziej odpowiednią odpowiedź na twoje pytanie, które brzmiało: „Path.Combine jest przydatny, czy istnieje podobna funkcja w ramach dla adresów URL?”
Ponieważ w strukturze URL istnieje podobna funkcja, proponuję poprawną metodę: „VirtualPathUtility.Combine”. Oto link referencyjny MSDN: Metoda VirtualPathUtility.Combine
Jest jedno zastrzeżenie: uważam, że działa to tylko w przypadku adresów URL związanych z witryną (tzn. Nie można jej używać do generowania linków do innej witryny. Na przykład var url = VirtualPathUtility.Combine("www.google.com", "accounts/widgets");
).
Server.MapPath
łączenia i łączenia.
Path.Combine("Http://MyUrl.com/", "/Images/Image.jpg").Replace("\\", "/")
path.Replace(Path.DirectorySeparatorChar, '/');
path.Replace(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar)
Właśnie stworzyłem małą metodę rozszerzenia:
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('/');
}
Można go użyć w następujący sposób:
"www.example.com/".UriCombine("/images").UriCombine("first.jpeg");
Dowcipny przykład, Ryan, kończący się linkiem do funkcji. Dobra robota.
Jedna rekomendacja Brian: jeśli zawiniesz ten kod w funkcji, możesz użyć UriBuilder do zawinięcia podstawowego adresu URL przed wywołaniem TryCreate.
W przeciwnym razie podstawowy adres URL MUSI zawierać schemat (gdzie UriBuilder przyjmie http: //). Tylko myśl:
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");
}
Łatwy sposób na ich połączenie i upewnienie się, że zawsze jest poprawny, to:
string.Format("{0}/{1}", Url1.Trim('/'), Url2);
Łączenie wielu części adresu URL może być nieco trudne. Możesz użyć konstruktora dwuparametrowego Uri(baseUri, relativeUri)
lub Uri.TryCreate()
funkcji narzędziowej.
W obu przypadkach może skończyć się powrotem niepoprawny wynik, ponieważ metody te przechowywać na obcinanie części względne off z pierwszego parametru baseUri
, czyli z czegoś podobnego http://google.com/some/thing
do http://google.com
.
Aby połączyć wiele części w końcowy adres URL, możesz skopiować dwie funkcje poniżej:
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;
}
Pełny kod z testami jednostkowymi w celu wykazania użycia można znaleźć na stronie https://uricombine.codeplex.com/SourceControl/latest#UriCombine/Uri.cs
Mam testy jednostkowe obejmujące trzy najczęstsze przypadki:
Uważam, że UriBuilder
działa naprawdę dobrze w tego typu rzeczach:
UriBuilder urlb = new UriBuilder("http", _serverAddress, _webPort, _filePath);
Uri url = urlb.Uri;
return url.AbsoluteUri;
Zobacz Klasa UriBuilder - MSDN, aby uzyskać więcej konstruktorów i dokumentacji.
Oto metoda 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);
}
Źródło: GitHub
Uważam, że następujące przydatne i ma następujące funkcje:
params
parametrów dla wielu segmentów adresu URLKlasa
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);
}
Testy
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
Moje ogólne rozwiązanie:
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;
}
Stworzyłem tę funkcję, która ułatwi Ci życie:
/// <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;
}
Działa zarówno w przypadku adresów URL, jak i zwykłych ścieżek.
Stosowanie:
// 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
Dlaczego nie skorzystać z poniższych.
System.IO.Path.Combine(rootUrl, subPath).Replace(@"\", "/")
[System.IO.Path]::Combine("http://MyUrl.com/","/Images/Image.jpg")
Jednak to nie powiedzie się z powodu: /Images/Image.jpg
. Usuń /
drugą ścieżkę podrzędną i zadziała:[System.IO.Path]::Combine("http://MyUrl.com/","Images/Image.jpg")
Reguły podczas łączenia adresów URL z identyfikatorem URI
Aby uniknąć dziwnego zachowania, należy przestrzegać jednej zasady:
string.Empty
usuwane ... dodanie ścieżki części spowoduje usunięcie katalogu względnego z adresu URL!Jeśli przestrzegasz powyższych zasad, możesz łączyć adresy URL z poniższym kodem. W zależności od sytuacji możesz dodać wiele części katalogu do adresu 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();
});
Jeśli nie chcesz dodawać zależności innej firmy, takiej jak Flurl, ani tworzyć niestandardowej metody rozszerzenia, w programie ASP.NET Core (dostępnym również w Microsoft.Owin), możesz użyć tego, PathString
co jest przeznaczone do budowania identyfikatora URI ścieżki Następnie możesz utworzyć pełny identyfikator URI, używając kombinacji tego Uri
i UriBuilder
.
W takim przypadku byłoby to:
new Uri(new UriBuilder("http", "MyUrl.com").Uri, new PathString("/Images").Add("/Image.jpg").ToString())
To daje wszystkie części składowe bez konieczności określania separatorów w podstawowym adresie URL. Niestety, PathString
wymaga, że /
jest dołączane do każdego łańcucha, w przeciwnym razie w rzeczywistości wyrzuca ArgumentException
! Ale przynajmniej możesz zbudować swój URI deterministycznie w sposób, który można łatwo przetestować jednostkowo.
Mam więc inne podejście, podobne do wszystkich, którzy korzystali z UriBuilder.
Nie chciałem podzielić moją baseURL (który może zawierać część ścieżki - np http://mybaseurl.com/dev/ ) jako javajavajavajavajava zrobił.
Poniższy fragment kodu pokazuje kod + testy.
Uwaga: to rozwiązanie obniża liczbę hostów i dodaje port. Jeśli nie jest to pożądane, można napisać ciąg znaków, np. Wykorzystując Uri
właściwość 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));
}
}
Testowane z .NET Core 2.1 w systemie Windows 10.
Dlaczego to działa?
Mimo że Path.Combine
zwróci ukośniki odwrotne (przynajmniej w systemie Windows), UriBuilder obsługuje tę sprawę w Setterze Path
.
Zaczerpnięte z https://github.com/dotnet/corefx/blob/master/src/System.Private.Uri/src/System/UriBuilder.cs (pamiętaj o wezwaniu do string.Replace
)
[AllowNull]
public string Path
{
get
{
return _path;
}
set
{
if ((value == null) || (value.Length == 0))
{
value = "/";
}
_path = Uri.InternalEscapeString(value.Replace('\\', '/'));
_changed = true;
}
}
Czy to najlepsze podejście?
Z pewnością to rozwiązanie jest dość samoopisujące (przynajmniej moim zdaniem). Ale polegasz na nieudokumentowanej (przynajmniej nie znalazłem nic dzięki szybkiemu wyszukiwaniu w Google) „funkcji” interfejsu API .NET. Może to ulec zmianie w przyszłej wersji, dlatego prosimy o objęcie metody testami.
Istnieją testy w https://github.com/dotnet/corefx/blob/master/src/System.Private.Uri/tests/FunctionalTests/UriBuilderTests.cs ( Path_Get_Set
), które sprawdzają, czy dane \
są poprawnie przekształcone.
Uwaga dodatkowa: Można również pracować UriBuilder.Uri
bezpośrednio z właściwością, jeśli identyfikator System.Uri
URI zostanie użyty dla ctor.
Dla każdego, kto szuka jednej linijki i po prostu chce połączyć części ścieżki bez tworzenia nowej metody lub odwoływania się do nowej biblioteki lub konstruowania wartości URI i konwertowania jej na ciąg, a następnie ...
string urlToImage = String.Join("/", "websiteUrl", "folder1", "folder2", "folder3", "item");
To dość proste, ale nie widzę, czego więcej potrzebujesz. Jeśli boisz się podwojonego „/”, możesz po prostu zrobić .Replace("//", "/")
później. Jeśli boisz się zastąpić podwójny „//” w „https: //”, to zamiast tego wykonaj jedno łączenie, zamień podwójne „/”, a następnie dołącz do adresu URL witryny (jednak jestem prawie pewien, że większość przeglądarek automatycznie przekonwertować wszystko za pomocą „https:” z przodu, aby odczytać w odpowiednim formacie). Wyglądałoby to tak:
string urlToImage = String.Join("/","websiteUrl", String.Join("/", "folder1", "folder2", "folder3", "item").Replace("//","/"));
Istnieje wiele odpowiedzi, które poradzą sobie z powyższymi, ale w moim przypadku potrzebowałem go tylko raz w jednym miejscu i nie będę musiał na nim polegać. Ponadto bardzo łatwo jest zobaczyć, co się tutaj dzieje.
Zobacz: https://docs.microsoft.com/en-us/dotnet/api/system.string.join?view=netframework-4.8
Posługiwać się:
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);
}
Ma tę zaletę, że zachowuje się dokładnie tak samo Path.Combine
.
Oto moje podejście i wykorzystam je również dla siebie:
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);
}
Użyj tego:
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);
}
}
Dla tego, co warto, oto kilka metod rozszerzenia. Pierwszy połączy ścieżki, a drugi doda parametry do adresu 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();
}
Jak stwierdzono w innych odpowiedziach, albo nowe, Uri()
albo TryCreate()
można zrobić tyknięcie. Jednak podstawowy Uri musi się kończyć, /
a krewny NIE musi zaczynać /
; w przeciwnym razie usunie końcową część podstawowego adresu URL
Myślę, że najlepiej to zrobić jako metodę rozszerzenia, tj
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);
}
i użyć go:
var baseUri = new Uri("http://test.com/test/");
var combinedUri = baseUri.Append("/Do/Something");
Pod względem wydajności zużywa więcej zasobów, niż potrzebuje, ze względu na klasę Uri, która wykonuje wiele analiz i weryfikacji; bardzo zgrubne profilowanie (Debugowanie) wykonało milion operacji w około 2 sekundy. Będzie to działać w większości scenariuszy, jednak aby być bardziej wydajnym, lepiej manipulować wszystkim jako ciągami, zajmuje to 125 milisekund na 1 milion operacji. To znaczy
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;
}
A jeśli nadal chcesz zwrócić identyfikator URI, zajmuje to około 600 milisekund na 1 milion operacji.
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);
}
Mam nadzieję, że to pomoże.
Myślę, że powinno to zapewnić większą elastyczność, ponieważ możesz poradzić sobie z tyloma segmentami ścieżki, ile chcesz:
public static string UrlCombine(this string baseUrl, params string[] segments)
=> string.Join("/", new[] { baseUrl.TrimEnd('/') }.Concat(segments.Select(s => s.Trim('/'))));
Url.Combine
metodę, która właśnie to robi.