Path.Combine很方便,但是.NET框架中是否有类似的URL函数?
我正在寻找这样的语法:
Url.Combine("http://MyUrl.com/", "/Images/Image.jpg")
这将返回:
"http://MyUrl.com/Images/Image.jpg"
Path.Combine很方便,但是.NET框架中是否有类似的URL函数?
我正在寻找这样的语法:
Url.Combine("http://MyUrl.com/", "/Images/Image.jpg")
这将返回:
"http://MyUrl.com/Images/Image.jpg"
当前回答
如果您不想拥有像Flurl这样的依赖项,可以使用它的源代码:
/// <summary>
/// Basically a Path.Combine for URLs. Ensures exactly one '/' separates each segment,
/// and exactly on '&' 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;
});
}
其他回答
组合URL的多个部分可能有点棘手。可以使用双参数构造函数Uri(baseUri、relativeUri),也可以使用Uri.TryCreate()实用程序函数。
在任何一种情况下,您都可能会返回错误的结果,因为这些方法会不断截断第一个参数baseUri的相对部分,例如http://google.com/some/thing到http://google.com.
为了能够将多个部分合并为最终URL,可以复制以下两个函数:
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;
}
完整的代码和单元测试可以在https://uricombine.codeplex.com/SourceControl/latest#UriCombine/Uri.cs
我有单元测试来涵盖三种最常见的情况:
Ryan Cook的答案与我的目标接近,可能更适合其他开发人员。然而,它在字符串的开头添加了http://,并且一般来说,它的格式比我想要的要多一些。
此外,对于我的用例,解析相对路径并不重要。
mdsharp的答案也包含了一个好主意的种子,尽管实际实现需要更多细节才能完成。这是一种充实它的尝试(我在生产中使用了这一点):
C#
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
这段代码通过了以下测试,恰好是在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
为什么不使用以下选项。
System.IO.Path.Combine(rootUrl, subPath).Replace(@"\", "/")
我发现UriBuilder在这种情况下工作得很好:
UriBuilder urlb = new UriBuilder("http", _serverAddress, _webPort, _filePath);
Uri url = urlb.Uri;
return url.AbsoluteUri;
有关更多构造函数和文档,请参阅UriBuilder类-MSDN。
我使用此代码来解决问题:
string[] brokenBaseUrl = Context.Url.TrimEnd('/').Split('/');
string[] brokenRootFolderPath = RootFolderPath.Split('/');
for (int x = 0; x < brokenRootFolderPath.Length; x++)
{
//if url doesn't already contain member, append it to the end of the string with / in front
if (!brokenBaseUrl.Contains(brokenRootFolderPath[x]))
{
if (x == 0)
{
RootLocationUrl = Context.Url.TrimEnd('/');
}
else
{
RootLocationUrl += String.Format("/{0}", brokenRootFolderPath[x]);
}
}
}