tags:

views:

4978

answers:

8

Path.Combine is handy, is there a similiar function in the 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"

...Of course string concatenation would be fine here since the '//' would be handled intelligently by the browser. But it feels a little less elegant.

+44  A: 

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

Ryan Cook
+1: This is good, although I have an irrational problem with the output parameter. ;)
Brian MacKay
This is a much better approach, as it will also work for paths of the form "../../something.html"
wsanville
@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.
Abel
+6  A: 

Witty example, Ryan, to end with a link to the function. Well done.

One recommendation Brian: if you wrap this code in a function, you may want to use a UriBuilder to wrap the base Url prior to the TryCreate call. Otherwise, the base url MUST include the Scheme (where the UriBuilder will assume http://). Just a thought:

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");
}
mtazva
you forgot the out in the TryCreate on the newUri parameter.Thanks for the nice solution, I made it an extension method on string.
Stephane
@Stephane: good suggestion, added (and removed the `= null` of `newUri` initialization).
Abel
+5  A: 

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);
}
mdsharpe
+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
See my answer for a more fully fleshed out version of this.
Brian MacKay
+34  A: 

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");
Joel
As 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.
Chris
Such an underused class.
Pat
I 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("http://test.com/mydirectory/"), "/helloworld.aspx").ToString() gives you "http://test.com/helloworld.aspx"; which would be incorrect if we wanted a Path.Combine style result.
DoctaJonez
It'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("http://test.com/mydirectory/"), "helloworld.aspx").ToString() results in "http://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.
Joel
+5  A: 

Based on the sample Url you provided I'm going to assume you want to combine Urls that are relative to your site.

Based on this assumption I'll propose this solution as the most appropriate response to your question which was: "Path.Combine is handy, is there a similar function in the framework for Urls?"

Since there the is a similar function in the framework for Urls I propose the correct is: "VirtualPathUtility.Combine" method. Here's the MSDN reference link: VirtualPathUtility.Combine Method

There is one caveat: I believe this only works for Urls relative to your site (i.e., you cannot use it to generate links to another web site. e.g., var url = VirtualPathUtility.Combine("www.google.com", "accounts/widgets");.

If I'm off base or missing something (which I often am) let me know... ;)

Jeronimo Colon III
+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
The 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.
Abel
+3  A: 

This question got some great, highly voted answers!

Ryan Cook's answer is close to what I'm after and may be more appropriate for other developers. However, it adds http:// to the beginning of the string and in general it does a bit more formatting than I'm after.

Also, for my use cases, resolving relative paths is not important.

mdsharp's answer also contains the seed of a good idea, although that actual implementation needed a few more details to be complete. This is an attempt to flesh it out (and I'm using this in production):

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("/\")
    url2 = url2.TrimStart("/\")

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

This code passes the following test:

<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
Brian MacKay
Thanks for expanding on this!
mdsharpe
Talking of details: what about the mandatory `ArgumentNullException("url1")` if the argument is `Nothing`? 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.
Abel
+1  A: 

I know this has been answered, but an easy way to combine them and ensure it's always correct is..

string.Format("{0}/{1}", Url1.Trim('/'), Url2);
Alex
+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! :)
Brian MacKay
A: 

I have to point out that Path.Combine appears to work for this also directly atleast on .NET4

Chris Marisic
If you use Path.Combine u will end up with something like this: www.site.com/foo\wrong\icon.png
Lehto