Path.Combine for URLs?
Solution 1
There is a Todd Menier's comment above that Flurl includes a Url.Combine
.
More details:
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"
Get Flurl.Http on NuGet:
PM> Install-Package Flurl.Http
Or get the stand-alone URL builder without the HTTP features:
PM> Install-Package Flurl
Solution 2
Uri
has a constructor that should do this for you: new Uri(Uri baseUri, string relativeUri)
Here's an example:
Uri baseUri = new Uri("http://www.contoso.com");
Uri myUri = new Uri(baseUri, "catalog/shownew.htm");
Note from editor: Beware, this method does not work as expected. It can cut part of baseUri in some cases. See comments and other answers.
Solution 3
This may be a suitably simple solution:
public static string Combine(string uri1, string uri2)
{
uri1 = uri1.TrimEnd('/');
uri2 = uri2.TrimStart('/');
return string.Format("{0}/{1}", uri1, uri2);
}
Solution 4
There's already some great answers here. Based on mdsharpe suggestion, here's an extension method that can easily be used when you want to deal with Uri instances:
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('/'))));
}
}
And usage example:
var url = new Uri("http://example.com/subpath/").Append("/part1/", "part2").AbsoluteUri;
This will produce http://example.com/subpath/part1/part2
If you want to work with strings instead of Uris then the following will also produce the same result, simply adapt it to suit your needs:
public string JoinUriSegments(string uri, params string[] segments)
{
if (string.IsNullOrWhiteSpace(uri))
return null;
if (segments == null || segments.Length == 0)
return uri;
return segments.Aggregate(uri, (current, segment) => $"{current.TrimEnd('/')}/{segment.TrimStart('/')}");
}
var uri = JoinUriSegements("http://example.com/subpath/", "/part1/", "part2");
Solution 5
You use 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);
}
Will return:
http://msdn.microsoft.com/en-us/library/system.uri.trycreate.aspx
Brian MacKay
Software engineer and entrepreneur with over 20 years of product development, line-of-business, and startup experience. Strong emphasis on execution and hands-on technical leadership. A decade of consulting experience provides a broad range of business knowledge, with industries touched spanning agriculture to yoga. Which is so close to an impressive A-to-Z set, so please let me know if you are aware of any zoos that need an app. :) Currently working in Microsoft's web stack including C#, Blazor, KendoUI, SQL Server, and Azure cloud architecture. Author, father, musician, expert-level chess player, decent kickboxer. I love games of all kinds. Above all, I'm a gigantic nerd and a friendly person.
Updated on July 08, 2022Comments
-
Brian MacKay almost 2 years
Path.Combine is handy, but is there a similar function in the .NET framework for URLs?
I'm looking for syntax like this:
Url.Combine("http://MyUrl.com/", "/Images/Image.jpg")
which would return:
"http://MyUrl.com/Images/Image.jpg"
-
Todd Menier about 10 yearsFlurl includes a
Url.Combine
method that does just that. -
Dave Gordon almost 10 yearsActually, the // is handled by the routing of the website or server and not by the browser. It will send what you put into the address bar. That's why we get problems when we type htp:// instead of http:// So the // can cause major problems on some sites. I am writing a .dll for a crawler which handles a particular website which throws a 404 if you have // in the url.
-
-
Brian MacKay over 14 years+1: This is good, although I have an irrational problem with the output parameter. ;)
-
Chris about 14 yearsAs a fan of using as much already built code as you can, I was wondering why no one had suggested this yet until I spotted your answer. This is, IMO, the best answer.
-
wsanville about 14 yearsThis is a much better approach, as it will also work for paths of the form "../../something.html"
-
Brian MacKay about 14 years+1 because it's close to what I'm looking for, although it would be ideal if it would work for any old url. I double it will get much more elegant than what mdsharpe proposed.
-
Brian MacKay about 14 years+1: Although this doesn't handle relative-style paths (../../whatever.html), I like this one for its simplicity. I would also add trims for the '\' character.
-
Brian MacKay about 14 yearsSee my answer for a more fully fleshed out version of this.
-
Brian MacKay about 14 years+1, although this is very similiar to mdsharpe's answer, which I improved upon in my answer. This version works great unless Url2 starts with / or \, or Url1 accidentally ends in \, or either one is empty! :)
-
Aisah Hamzah over 13 years@Brian: if it helps, all TryXXX methods (
int.TryParse
,DateTime.TryParseExact
) have this output param to make it easier to use them in an if-statement. Btw, you don't have to initialize the variable as Ryan did in this example. -
Aisah Hamzah over 13 yearsThe caveat is correct, it cannot work with absolute uris and the result is always relative from the root. But it has an added benefit, it processes the tilde, as with "~/". This makes it a shortcut for
Server.MapPath
and combining. -
Aisah Hamzah over 13 yearsTalking of details: what about the mandatory
ArgumentNullException("url1")
if the argument isNothing
? Sorry, just being picky ;-). Note that a backslash has nothing to do in a URI (and if it is there, it should not be trimmed), so you can remove that from your TrimXXX. -
Lehto over 13 yearsIf you use Path.Combine u will end up with something like this: www.site.com/foo\wrong\icon.png
-
Doctor Jones over 13 yearsI like the use of the Uri class, unfortunately it will not behave like Path.Combine as the OP asked. For example new Uri(new Uri("test.com/mydirectory/"), "/helloworld.aspx").ToString() gives you "test.com/helloworld.aspx"; which would be incorrect if we wanted a Path.Combine style result.
-
Joel Beckham over 13 yearsIt's all in the slashes. If the relative path part starts with a slash, then it behaves as you described. But, if you leave the slash out, then it works the way you'd expect (note the missing slash on the second parameter): new Uri(new Uri("test.com/mydirectory/"), "helloworld.aspx").ToString() results in "test.com/mydirectory/helloworld.aspx". Path.Combine behaves similarly. If the relative path parameter starts with a slash, it only returns the relative path and doesn't combine them.
-
Carl over 13 yearsIf your baseUri happened to be "test.com/mydirectory/mysubdirectory" then the result would be "test.com/mydirectory/helloworld.aspx" instead of "test.com/mydirectory/mysubdirectory/helloworld.aspx". The subtle difference is the lack of trailing slash on the first parameter. I'm all for using existing framework methods, if I have to have the trailing slash there already then I think that doing partUrl1 + partUrl2 smells a lot less - I could've potentially been chasing that trailing slash round for quite a while all for the sake of not doing string concat.
-
nickd about 13 yearsThe only reason I want a URI combine method is so that I don't have to check for the trailing slash. Request.ApplicationPath is '/' if your application is at the root, but '/foo' if it's not.
-
Baptiste Pernet about 13 yearsI -1 this answer because this doesn't answer the problem. When you want to combine url, like when you want to use Path.Combine, you don't want to care about the trailing /. and with this, you have to care. I prefer solution of Brian MacKay or mdsharpe above
-
Joel Beckham about 13 years@Baptiste Pernet: Good point - in general you are correct. In the OP's specific case, where the baseUri doesn't have any additional path elements, the trailing slash does not have any effect on the result. If there are additional path elements in the baseUri, e.g. "Http://MyUrl.com/some/folder", then you're right, you do have to pay attention to keep the trailing slash there.
-
Brian MacKay almost 13 years+1: Now we're talking... I'm going to try this out. This might even end up being the new accepted answer. After trying to new Uri() method I really don't like it. Too finnicky.
-
angularsen over 12 yearsThis solution makes it trivial to write a UriUtils.Combine("base url", "part1", "part2", ...) static method that is very similar to Path.Combine(). Nice!
-
angularsen over 12 yearsTo support relative URIs I had to use ToString() instead of AbsoluteUri and UriKind.AbsoluteOrRelative in the Uri constructor.
-
Ales Potocnik Hahonina over 12 yearsThanks for the tip about relative Uris. Unfortunately Uri doesn't make it easy to deal with relative paths as there is always some mucking about with Request.ApplicationPath involved. Perhaps you could also try using new Uri(HttpContext.Current.Request.ApplicationPath) as a base and just call Append on it? This will give you absolute paths but should work anywhere within site structure.
-
Jaider almost 12 yearsyou can use params string[] and recursively join them to allow more than 2 combinations
-
Jaider almost 12 years
path.Replace(Path.DirectorySeparatorChar, '/');
-
Gromer almost 12 yearsThis is exactly what I needed! Was not a fan of having to care where I put trailing slashes, etc...
-
SliverNinja - MSFT almost 12 years
path.Replace(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar)
-
Brian MacKay over 11 yearsNice touch with 'WebPath'. :) The code might be unecessarily dense though - it's hard for me to glance at this and say, yes, that's perfect. It makes me want to see unit tests. Maybe that's just me!
-
penguat over 11 yearsx.StartsWith("/") && !x.StartsWith("http") - why the http check? what do you gain?
-
Vlax over 11 yearsIt's a good solution but it doesn't address following cases:
Assert.IsTrue(target.UrlCombine("../test1", "/") = "../test1/") Assert.IsTrue(target.UrlCombine("/", "test1/") = "/test1/")
-
Brian MacKay over 11 yearsI see what you mean about the tests. Since the code doesn't actually work in terms of chopping things up into directories and then gluing them back together and instead just uses text, I didn't even think to add them. They would pass as they are basically covered... You can feel free to edit the code if you want.
-
Vlax over 11 yearsSorry I was wrong your code does indeed covers the mentioned case!
-
Mark Hurd about 11 yearsNot too useful really. There's a number of Google hits explaining some of its issues, but, as well as not liking "http://..." at the start, it actually removes the last sub path of the first argument if it doesn't end in a "/"! The MSDN description sounds fine though!
-
Martin Murphy about 11 yearsYou don't want to try to strip off the slash if it starts with http.
-
Martin Murphy about 11 years@BrianMacKay, I'm not sure a two liner warrants a unit test but if you like feel free to provide one. It's not like I'm accepting patches or anything, but feel free to edit the suggestion.
-
Per G about 11 yearsTo get it to wrk u must remove first / in second arg ie "/Images" - / Path.Combine("Http://MyUrl.com", "Images/Image.jpg")
-
Bogdan_Ch almost 11 yearsThis code works wrong if you deploy application in a virtual path, not in the root of web site. If your base URL will be my.website.com/virtualdir and relative pathe will be catalog/page.html, this will return my.website.com/catalog/page.html, vitrualdir is missed
-
teashark over 10 yearsThis answer suffers the same problem as Joel's: joining
test.com/mydirectory/
and/helloworld.aspx
will result intest.com/helloworld.aspx
which is seemingly not what you want. -
Faisal Mq over 10 yearsHi, this failed for following : if (Uri.TryCreate(new Uri("localhost/MyService/"), "/Event/SomeMethod?abc=123", out result)) { Console.WriteLine(result); } It is showing me result as : localhost/Event/SomeMethod?abc=123 Note: "http://" is replaced from base Uri here by stackoverflow
-
Tom Lint over 10 years@FaisalMq This is the correct behavior, since you passed a root-relative second parameter. If you had left out the leading / on the second parameter, you'd have gotten the result you expected.
-
NightOwl888 about 10 years+1 for rolling in the null checking so it won't blow up.
-
user247702 about 10 years@SliverNinja That's not correct The value of this field is a backslash ('\') on UNIX, and a slash ('/') on Windows and Macintosh operating systems. When using Mono on a Linux system, you'd get the wrong separator.
-
Daniel Liuzzi about 10 yearsI used to be a big fan of this approach, and was using it everywhere, then it bit me hard when I found out about
Uri
class's nasty habit of URL decoding querystring parameters. Even worse, it is by design. See stackoverflow.com/a/7307950. As much as I hate it, I now do all URL concatenation manually. At least it won't mess up my returlUrl's. -
Believe2014 about 10 yearsI have explained and provided a solution to this problem in my answer stackoverflow.com/a/23399048/3481183
-
Believe2014 about 10 yearsExactly. I have spent some time implementing the Uri.Combine function for this exact reason: stackoverflow.com/a/23399048/3481183
-
Believe2014 about 10 yearsYou could have used VirtualPathUtiliy class to append and remove trailing slashes safely. Check out my answer: stackoverflow.com/a/23399048/3481183
-
Believe2014 about 10 yearsThis is acceptable only for your case. There are cases which could broke your code. Also, you didn't do proper encoding of the parts of the path. This could be a huge vulnerability when it comes to cross site scripting attack.
-
Amit Bhagat about 10 yearsI agree to your points. The code is supposed to do just simple combining of two url parts.
-
Brian MacKay about 10 years@LouisRhys Because I was working on a VB.net project at the time, it happens. ;) But, I updated this with a C# translation.
-
Brian MacKay about 10 years+1 for all the extra effort. I need to maintain this question a bit for some of the higher voted answers, you have thrown down the gauntlet. ;)
-
Martin Capodici about 10 yearsThis answer doesn't deliver the goods when the first URI isn't the root of the domain. Shame.
-
Uriah Blatherwick almost 10 yearsI sure wish this was in the Base Class Library like Path.Combine.
-
Jack over 9 yearsAwesome! it even work in that
Uri baseUri = new Uri("http://www.foo.com/lol"); Uri myUri = new Uri(baseUri, "../test");
andmyUri.ToString()
givehttp://www.foo.com/test
-
Joshua C over 8 yearsSlashes or not, this does not seem to work for urls such as
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
-
BlueRaja - Danny Pflughoeft over 8 years-1
new Uri(new Uri("test.com/mydirectory"), "helloworld.aspx")
returnstest.com/helloworld.aspx
, which is not what anyone anywhere wants. -
Brian MacKay over 8 yearsIt looks like this might be for paths, rather than URLs.
-
Admin over 8 years@BrianMacKay Agreed that it looks like it, but it's from the UrlUtility class and used in the context of combining URLs
-
Admin over 8 yearsEdited to clarify what class it belongs to
-
Tom Lint over 8 years@DoctorJones This is the correct behavior. Path.Combine will take any parameter with leading \ as the new root, overriding any previously specified root. So the Uri constructor actually does the right thing. This 'feature' of Path.Combine has bitten me quite a few times in the past.
-
Doctor Jones over 8 years@TomLint I understand that it works that way by design. I was stating that it behaves differently to Path.Combine, because OP asked for a Path.Combine equivalent for URLs. The Uri constructor is not an equivalent of Path.Combine.
-
Tom Lint over 8 years@DoctorJones I don't think you completely understood what I said; Path.Combine has exactly the same behavior with regard to parameters prefixed by a path separator character. Therefore the combining of the Uris this way is correct, and exactly what the OP asked for.
-
n.podbielski over 8 yearsI also added check if any of paths to append are not null nor empty string.
-
Reda over 8 yearsThis doesn't behave as the op asked
-
mike about 8 yearsSurprising that none of the other massive upvoters mentioned that the other solutions work for two directories/names only.
-
LB2 almost 8 yearsMost fundamentally, this doesn't work if base path is a relative one and doesn't have all the necessary info to make it an Absolute URI. So combining
/a/b/c/
withd/e/f
with throwArgumentOutOfRangeException
. Not at all usable for the purpose. -
JJS almost 8 years@MarkHurd, I agree. I didn't realize it was Char(), I thought it was a single Char. Makes sense why they did it this way. I'd prefer
New Char() { "/"c, "\"c }
-
JJS almost 8 years@MarkHurd I edited the code again, so that it's behaviorally the same as the C#, and syntactically equivalent as well.
-
Brian MacKay almost 8 years@JJS Looks like someone broke this and you came back and fixed it -good job. :)
-
JJS almost 8 years@BrianMacKay i broke it, markhurd pointed out my mistake and rolled back, i updated again... cheers
-
JeremyWeir almost 8 yearsAll yall that are geeking out on the Directory Separator are forgetting that the strings could have come from a different OS than you are on now. Just replace backslash with forward slash and you're covered.
-
Ilya Serbis over 7 yearsSame problem as in the top answer:
Uri.TryCreate(new Uri("http://test.com/mydirectory"), "helloworld.aspx", out result)
will set result tohttp://test.com/helloworld.aspx
-
Florian Fida over 7 years@Lu55 if you to put a
/
after the directory it will work. -
Adam Plocher about 7 yearsURLs are abstractions that MAY translate to a filesystem path, but shouldn't be thought of that way. This will never be able to function like Path.Combine without making some major assumptions. If you have a URL: blah.com/thing1/thing2 whose to say that thing2 is a dir or a file? If you try to combine that with a relative path of "../thing3" should it resolve to blah.com/thing1/thing3 or blah.com/thing3? Now is thing3 a dir or file? Without explicitly adding the trailing slashes MANUALLY- or some other custom defined parsing rule, it won't work. Most webservers won't expose that info.
-
Shiva almost 7 yearsThis helper method is very flexible and works well in many different use cases. Thank you!
-
Harry Berry over 6 yearsTake care when using this Class, the rest of the class contains SharePoint specific artifacts.
-
Underverse over 6 yearsI was looking for the PowerShell version of this which would be:
[System.IO.Path]::Combine("http://MyUrl.com/","/Images/Image.jpg")
however this fails with a result of:/Images/Image.jpg
. Remove the/
from the second subPath and it works:[System.IO.Path]::Combine("http://MyUrl.com/","Images/Image.jpg")
-
pholpar about 6 yearsNice idea, but it fails, when one of the parameter is null.
-
Brian MacKay over 5 yearsWell, this question gets a lot of traffic, and the answer with 1000+ upvotes does not actually work in all cases. Years later, I actually use Flurl for this, so I am accepting this one. It seems to work in all cases I have encountered. If people don't want to take a dependency, I posted an answer that also works fine.
-
lizzy91 over 5 yearsand if you dont use
Flurl
and would perfer a lightweight version, github.com/jean-lourenco/UrlCombine -
Simon about 5 yearsThis solution generates an warning: docs.microsoft.com/de-de/visualstudio/code-quality/…
-
PRMan almost 5 yearsCount() should be Length so that you don't need to include Linq in your library just for that.
-
aggsol over 4 yearsThis is a very reliable approach. Thumbs up for the unit test!!
-
Mahmoud Hanafy over 4 yearsFor this to work, the base Uri has to have a trailing slash, and the relative Uri has to NOT have a leading slash. i.e. Base: "test.com/test" Relative: "do/did/done" result: "test.com/test/do/did/done" This is also equivalent to u = new Uri(new Uri("test.com/test/"), "/do/did/done");
-
Mohammad Almasi over 4 yearsIf we have such a url the result will be wrong Example : 127.0.0.1:8080/drupal
-
Moriya over 4 yearsSome issues with the tests: // Result = test1/test2/test3\ for the 4th one and the last of the throws tests gives ArgumentNullException instead of ArgumentException
-
ThePeter about 4 yearsThis was exactly what i was looking for.
-
Robert McKee almost 4 yearsI view this as a correct answer. A URI may not refer to a directory. It always refers to a resource, which is the last part given unless it ends in a /, in which case it refers to the default resource in that path. "localhost/directory/subdirectory" is therefore a false assumption because "subdirectory" is actually a resource (think file), not a collection of resources. This is just how URIs work, and combining that with a relative path should always strip the "subdirectory" part out. And that is exactly what the Uri constructor does.
-
Arvo Bowen over 3 yearsAs I was looking at all the answers I was like... "Why has no one posted an extension method yet, I'm going to post one"... Never mind. +1
-
Mladen B. about 3 years@BrianMacKay, OP never asked for relative-style paths...
-
Brian MacKay about 3 years@MladenB. Well, I am the OP. :) Although I did not explicitly ask for it, the need to support relative-style paths is an inherent part of the overarching problem domain... Failing to do so can lead to confusing results if people try to re-use this.
-
Slate almost 3 yearsNote will chop a forward slash from the protocol, if you specify that separate from the host, e.g. ("https://", Host, Endpoint)
-
MickyD about 2 years2022: Whilst an OK solution, it's probably inadvisable for use for URLs in the same way
string
is for file and folder paths (where you would usePath.xxx()
instead) -
vibs2006 almost 2 yearsMost Clean method!