Path.Combine很方便,但是.NET框架中是否有类似的URL函数?

我正在寻找这样的语法:

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

这将返回:

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


当前回答

我必须指出,Path.Combine似乎也能直接实现这一点,至少在.NET 4上是如此。

其他回答

Path.Combine("Http://MyUrl.com/", "/Images/Image.jpg").Replace("\\", "/")

如果您不想拥有像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;
        });
    }

上面有托德·梅尼尔(Todd Menier)的评论,Flurl包括一个Url.Combine。

更多详情:

Url.Combine基本上是Url的Path.Combine,确保部分之间只有一个分隔符:

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" 

在NuGet上获取Flurl.Http:

PM>安装包Flurl.Http

或者获取没有HTTP功能的独立URL生成器:

PM>安装程序包Flurl

我们使用以下简单的助手方法将任意数量的URL部分连接在一起:

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

注意,它不支持'..//something/page.htm'样式的相对URL!

我结合了前面的所有答案:

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