Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Combining two relative Uris

Tags:

c#

.net

uri

I need to combine two relative Uris, e.g. ../mypath/ and myimage.png to create ../mypath/myimage.png. They are not paths to files on disk so Path.Combine is not appropriate (they are relative paths to resources for a web page). new Uri throws an ArgumentOutOfRangeException because the base uri is relative (not absolute).

Do I have any options other than checking for a trailing slash and then combining the paths myself?

EDIT:

Here is a test case that demonstrates that Path.Combine will not work for the case when the first url does not already contain a trailing slash:

// The first case fails with result "../testpath\resource.png"
[TestCase("../testpath", "resource.png", "../testpath/resource.png")] 
[TestCase("../testpath/", "resource.png", "../testpath/resource.png")]
public void TestPathCombine(string path, string resourceName, string expectedResult) {
    string result = Path.Combine(path, resourceName);
    Assert.AreEqual(expectedResult, result);
}
like image 812
Lawrence Johnston Avatar asked Dec 08 '10 17:12

Lawrence Johnston


2 Answers

If your second part is (like in my own case) really a file name without any escaping (and as such may contain invalids chars for an url), here is the solution I have ended up with:

VirtualPathUtility.Combine(
    VirtualPathUtility.AppendTrailingSlash(relativeUri), 
    Uri.EscapeDataString(fileName));

Beware that this solution will not support a full uri (with scheme, host, port): it will throw an exception with a full uri. Thanks to Manish Pansiniya for mentioning System.Web.VirtualPathUtility.

In addition, as my file name was in fact a partial file path (some folder names followed by file name), rather than calling directly System.Uri.EscapeDataString, I have call the following function:

/// <summary>
/// Convert a partial file path to a partial url path.
/// </summary>
/// <param name="partialFilePath">The partial file path.</param>
/// <returns>A partial url path.</returns>
public static string ConvertPartialFilePathToPartialUrlPath(
    string partialFilePath)
{
    if (partialFilePath == null)
        return null;
    return string.Join("/", 
        partialFilePath.Split('/', '\\')
            .Select(part => Uri.EscapeDataString(part)));
}

(Requires using System.Linq for .Select and fx4 for the used string.Join overload.)

like image 172
Frédéric Avatar answered Nov 19 '22 13:11

Frédéric


You can use the Uri constructor that takes a base and a relative part to do the combination - but note that the behavior will possibly not be what you expect. The Uri class will see the end part of your base as either a "directory" or a "file" (to put it in path terms). If it sees the end as a file, that will get removed.

For example, combining http://server/something/ with resource.png will give you http://server/something/resource.png.

Now omit the trailing slash: combine http://server/something with resource.png and get http://server/resource.png.

This makes sense if you think of it as starting with a base Uri of http://server/something.png and asking for the relative uri resource.png: http://server/something.png/resource.png isn't what you're looking for.

If you ALWAYS know that they should be appended, you need to make sure that the base ends with a slash before combining.

like image 3
Philip Rieck Avatar answered Nov 19 '22 13:11

Philip Rieck