c# - navegador - Path.Combine para URLs?
request.url.scheme c# (30)
¿Por qué no solo usar lo siguiente?
System.IO.Path.Combine(rootUrl, subPath).Replace(@"/", "/")
Path.Combine es útil, pero ¿hay una función similar en el marco .NET para las URLs ?
Estoy buscando una sintaxis como esta:
Url.Combine("http://MyUrl.com/", "/Images/Image.jpg")
que volvería:
"http://MyUrl.com/Images/Image.jpg"
Acabo de armar un pequeño método de extensión:
public static string UriCombine (this string val, string append)
{
if (String.IsNullOrEmpty(val)) return append;
if (String.IsNullOrEmpty(append)) return val;
return val.TrimEnd(''/'') + "/" + append.TrimStart(''/'');
}
Se puede usar así:
"www.example.com/".UriCombine("/images").UriCombine("first.jpeg");
Aquí está el método UrlUtility.Combine de Microsoft (OfficeDev PnP):
const char PATH_DELIMITER = ''/'';
/// <summary>
/// Combines a path and a relative path.
/// </summary>
/// <param name="path"></param>
/// <param name="relative"></param>
/// <returns></returns>
public static string Combine(string path, string relative)
{
if(relative == null)
relative = String.Empty;
if(path == null)
path = String.Empty;
if(relative.Length == 0 && path.Length == 0)
return String.Empty;
if(relative.Length == 0)
return path;
if(path.Length == 0)
return relative;
path = path.Replace(''//', PATH_DELIMITER);
relative = relative.Replace(''//', PATH_DELIMITER);
return path.TrimEnd(PATH_DELIMITER) + PATH_DELIMITER + relative.TrimStart(PATH_DELIMITER);
}
Fuente: UrlUtility.Combine
Aquí está mi enfoque y lo usaré para mí también:
public static string UrlCombine(string part1, string part2)
{
string newPart1 = string.Empty;
string newPart2 = string.Empty;
string seperator = "/";
// If either part1 or part 2 is empty,
// we don''t need to combine with seperator
if (string.IsNullOrEmpty(part1) || string.IsNullOrEmpty(part2))
{
seperator = string.Empty;
}
// If part1 is not empty,
// remove ''/'' at last
if (!string.IsNullOrEmpty(part1))
{
newPart1 = part1.TrimEnd(''/'');
}
// If part2 is not empty,
// remove ''/'' at first
if (!string.IsNullOrEmpty(part2))
{
newPart2 = part2.TrimStart(''/'');
}
// Now finally combine
return string.Format("{0}{1}{2}", newPart1, seperator, newPart2);
}
Bueno, acabo de concatenar dos cadenas y uso expresiones regulares para hacer la parte de limpieza.
public class UriTool
{
public static Uri Join(string path1, string path2)
{
string url = path1 + "/" + path2;
url = Regex.Replace(url, "(?<!http:)/{2,}", "/");
return new Uri(url);
}
}
Entonces, puedes usarlo así:
string path1 = "http://someaddress.com/something/";
string path2 = "/another/address.html";
Uri joinedUri = UriTool.Join(path1, path2);
// joinedUri.ToString() returns "http://someaddress.com/something/another/address.html"
Combinar varias partes de una URL puede ser un poco complicado. Puede usar el constructor de dos parámetros Uri(baseUri, relativeUri)
, o puede usar la función de utilidad Uri.TryCreate()
.
En cualquier caso, podría terminar devolviendo un resultado incorrecto porque estos métodos siguen truncando las partes relativas del primer parámetro baseUri
, es decir, de algo como http://google.com/some/thing
a http://google.com
.
Para poder combinar varias partes en una URL final, puede copiar las dos funciones a continuación:
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;
}
El código completo con pruebas unitarias para demostrar el uso se puede encontrar en https://uricombine.codeplex.com/SourceControl/latest#UriCombine/Uri.cs
Tengo pruebas unitarias para cubrir los tres casos más comunes:
Creé esta función que hará tu vida más fácil:
// Fixes internal paths
Console.WriteLine(PathCombine(true , true , true , @"////folder 1//////////folder2////folder3///" , @"//somefile.ext//////"));
// Result: /folder 1/folder2/folder3/somefile.ext
// Doesn''t fix internal paths
Console.WriteLine(PathCombine(true , true , false , @"////folder 1//////////folder2////folder3///" , @"//somefile.ext//////"));
//result : /folder 1//////////folder2////folder3/somefile.ext
// Don''t worry about URL prefixes when fixing internal paths
Console.WriteLine(PathCombine(true , false , true , @"/////https://////lul.com//////////folder2////folder3///" , @"//somefile.ext//////"));
// Result: https://lul.com/folder2/folder3/somefile.ext
Console.WriteLine(PathCombine(false , true , true , @"../../..///../..././../somepath" , @"anotherpath"));
// Result: /../../../../..././../somepath/anotherpath
Funciona para las direcciones URL, así como las rutas normales.
Uso:
var url = Url.Combine(
"http://foo.com/",
"/too/", "/many/", "/slashes/",
"too", "few?",
"x=1", "y=2"
// result: "http://www.foo.com/too/many/slashes/too/few?x=1&y=2"
Ejemplo ingenioso, Ryan, para terminar con un enlace a la función. Bien hecho.
Una recomendación Brian: si envuelve este código en una función, es posible que desee usar un UriBuilder para envolver la URL base antes de la llamada TryCreate.
De lo contrario, la URL base DEBE incluir el esquema (donde UriBuilder asumirá http: //). Solo un pensamiento:
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");
}
En función de la URL muestra que proporcionó, asumiré que desea combinar las URL que son relativas a su sitio.
Sobre la base de este supuesto, propondré esta solución como la respuesta más adecuada a su pregunta, que fue: "Path.Combine es útil, ¿existe una función similar en el marco para las URL?"
Dado que hay una función similar en el marco para las URL, propongo que la correcta es: método "VirtualPathUtility.Combine". Aquí está el enlace de referencia de MSDN: Método VirtualPathUtility.Combine
Hay una advertencia: creo que esto solo funciona para las URL relacionadas con su sitio (es decir, no puede usarlo para generar enlaces a otro sitio web. Por ejemplo, var url = VirtualPathUtility.Combine("www.google.com", "accounts/widgets");
).
Encontré que UriBuilder
funcionó muy bien para este tipo de cosas:
UriBuilder urlb = new UriBuilder("http", _serverAddress, _webPort, _filePath);
Uri url = urlb.Uri;
return url.AbsoluteUri;
Ver UriBuilder Class - MSDN para más constructores y documentación.
Esta puede ser una solución adecuadamente simple:
public static string Combine(string uri1, string uri2)
{
uri1 = uri1.TrimEnd(''/'');
uri2 = uri2.TrimStart(''/'');
return string.Format("{0}/{1}", uri1, uri2);
}
Hay un comentario de Todd Menier que Flurl incluye una Url.Combine.
Más detalles:
Url.Combine es básicamente una Path.Combine para URL, asegurando un solo y único carácter separador entre las partes:
/// <summary>
/// The ultimate Path combiner of all time
/// </summary>
/// <param name="IsURL">
/// true - if the paths are Internet URLs, false - if the paths are local URLs, this is very important as this will be used to decide which separator will be used.
/// </param>
/// <param name="IsRelative">Just adds the separator at the beginning</param>
/// <param name="IsFixInternal">Fix the paths from within (by removing duplicate separators and correcting the separators)</param>
/// <param name="parts">The paths to combine</param>
/// <returns>the combined path</returns>
public static string PathCombine(bool IsURL , bool IsRelative , bool IsFixInternal , params string[] parts)
{
if (parts == null || parts.Length == 0) return string.Empty;
char separator = IsURL ? ''/'' : ''//';
if (parts.Length == 1 && IsFixInternal)
{
string validsingle;
if (IsURL)
{
validsingle = parts[0].Replace(''//' , ''/'');
}
else
{
validsingle = parts[0].Replace(''/'' , ''//');
}
validsingle = validsingle.Trim(separator);
return (IsRelative ? separator.ToString() : string.Empty) + validsingle;
}
string final = parts
.Aggregate
(
(string first , string second) =>
{
string validfirst;
string validsecond;
if (IsURL)
{
validfirst = first.Replace(''//' , ''/'');
validsecond = second.Replace(''//' , ''/'');
}
else
{
validfirst = first.Replace(''/'' , ''//');
validsecond = second.Replace(''/'' , ''//');
}
var prefix = string.Empty;
if (IsFixInternal)
{
if (IsURL)
{
if (validfirst.Contains("://"))
{
var tofix = validfirst.Substring(validfirst.IndexOf("://") + 3);
prefix = validfirst.Replace(tofix , string.Empty).TrimStart(separator);
var tofixlist = tofix.Split(new[] { separator } , StringSplitOptions.RemoveEmptyEntries);
validfirst = separator + string.Join(separator.ToString() , tofixlist);
}
else
{
var firstlist = validfirst.Split(new[] { separator } , StringSplitOptions.RemoveEmptyEntries);
validfirst = string.Join(separator.ToString() , firstlist);
}
var secondlist = validsecond.Split(new[] { separator } , StringSplitOptions.RemoveEmptyEntries);
validsecond = string.Join(separator.ToString() , secondlist);
}
else
{
var firstlist = validfirst.Split(new[] { separator } , StringSplitOptions.RemoveEmptyEntries);
var secondlist = validsecond.Split(new[] { separator } , StringSplitOptions.RemoveEmptyEntries);
validfirst = string.Join(separator.ToString() , firstlist);
validsecond = string.Join(separator.ToString() , secondlist);
}
}
return prefix + validfirst.Trim(separator) + separator + validsecond.Trim(separator);
}
);
return (IsRelative ? separator.ToString() : string.Empty) + final;
}
He combinado todas las respuestas anteriores:
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);
}
La respuesta de Ryan Cook es cercana a lo que busco y puede ser más apropiada para otros desarrolladores. Sin embargo, agrega http: // al principio de la cadena y, en general, hace un poco más de formato del que busco.
Además, para mis casos de uso, resolver rutas relativas no es importante.
La respuesta de mdsharp también contiene la semilla de una buena idea, aunque esa implementación real necesitaba algunos detalles más para completarla. Este es un intento de concretarlo (y lo estoy usando en producción):
DO#
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
Este código pasa la siguiente prueba, que está en 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
Me parece útil lo siguiente y tiene las siguientes características:
- Tiros en espacio nulo o blanco.
- Toma múltiples
params
parametros para multiples segmentos de url - arroja en nulo o vacío
Clase
public static class UrlPath
{
private static string InternalCombine(string source, string dest)
{
if (string.IsNullOrWhiteSpace(source))
throw new ArgumentException("Cannot be null or white space", nameof(source));
if (string.IsNullOrWhiteSpace(dest))
throw new ArgumentException("Cannot be null or white space", nameof(dest));
return $"{source.TrimEnd(''/'', ''//')}/{dest.TrimStart(''/'', ''//')}";
}
public static string Combine(string source, params string[] args)
=> args.Aggregate(source, InternalCombine);
}
Pruebas
UrlPath.Combine("test1", "test2");
UrlPath.Combine("test1//", "test2");
UrlPath.Combine("test1", "/test2");
// Result = test1/test2
UrlPath.Combine(@"test1//////", @"///////////test2", @"///////////test3/") ;
// Result = test1/test2/test3
UrlPath.Combine("/test1/", "/test2/", null);
UrlPath.Combine("", "/test2/");
UrlPath.Combine("/test1/", null);
// Throws an ArgumentException
Mi solución genérica:
public static string Combine(params string[] uriParts)
{
string uri = string.Empty;
if (uriParts != null && uriParts.Any())
{
char[] trims = new char[] { ''//', ''/'' };
uri = (uriParts[0] ?? string.Empty).TrimEnd(trims);
for (int i = 1; i < uriParts.Length; i++)
{
uri = string.Format("{0}/{1}", uri.TrimEnd(trims), (uriParts[i] ?? string.Empty).TrimStart(trims));
}
}
return uri;
}
Path.Combine no funciona para mí porque puede haber caracteres como "|" en los argumentos QueryString y, por lo tanto, en la URL, lo que dará como resultado una ArgumentException.
Primero probé el nuevo Uri(Uri baseUri, string relativeUri)
, que falló en mi caso debido a las URI como http://www.mediawiki.org/wiki/Special:SpecialPages
:
new Uri(new Uri("http://www.mediawiki.org/wiki/"), "Special:SpecialPages")
resultará en Special: SpecialPages, debido a los dos puntos después de Special
que denota un esquema.
Así que finalmente tuve que tomar la ruta mdsharpe / Brian MacKays y la desarrollé un poco más para trabajar con varias partes de URI:
public static string CombineUri(params string[] uriParts)
{
string uri = string.Empty;
if (uriParts != null && uriParts.Count() > 0)
{
char[] trims = new char[] { ''//', ''/'' };
uri = (uriParts[0] ?? string.Empty).TrimEnd(trims);
for (int i = 1; i < uriParts.Count(); i++)
{
uri = string.Format("{0}/{1}", uri.TrimEnd(trims), (uriParts[i] ?? string.Empty).TrimStart(trims));
}
}
return uri;
}
Uso: CombineUri("http://www.mediawiki.org/", "wiki", "Special:SpecialPages")
Un simple trazador de líneas:
public static string Combine(this string uri1, string uri2) => $"{uri1.TrimEnd(''/'')}/{uri2.TrimStart(''/'')}";
Inspirado en la respuesta de @Matt Sharpe.
Una forma fácil de combinarlos y asegurar que siempre sea correcta es:
string.Format("{0}/{1}", Url1.Trim(''/''), Url2);
Utilicé este código para resolver el problema:
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]);
}
}
}
Utiliza 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);
}
Volverá
http://msdn.microsoft.com/en-us/library/system.uri.trycreate.aspx
Utilizar esta:
public static class WebPath
{
public static string Combine(params string[] args)
{
var prefixAdjusted = args.Select(x => x.StartsWith("/") && !x.StartsWith("http") ? x.Substring(1) : x);
return string.Join("/", prefixAdjusted);
}
}
Utilizar:
private Uri UriCombine(string path1, string path2, string path3 = "", string path4 = "")
{
string path = System.IO.Path.Combine(path1, path2.TrimStart(''//', ''/''), path3.TrimStart(''//', ''/''), path4.TrimStart(''//', ''/''));
string url = path.Replace(''//',''/'');
return new Uri(url);
}
Tiene el beneficio de comportarse exactamente como Path.Combine
.
Ya hay algunas grandes respuestas aquí. Basado en la sugerencia de mdsharpe, aquí hay un método de extensión que puede usarse fácilmente cuando se quiere tratar con instancias de Uri:
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(''/''))));
}
}
Y ejemplo de uso:
var url = new Uri("http://example.com/subpath/").Append("/part1/", "part2").AbsoluteUri;
Esto producirá http://example.com/subpath/part1/part2
Uri
tiene un constructor que debería hacer esto por ti: new Uri(Uri baseUri, string relativeUri)
Aquí hay un ejemplo:
Uri baseUri = new Uri("http://www.contoso.com");
Uri myUri = new Uri(baseUri, "catalog/shownew.htm");
Reglas al combinar URLs con un URI
Para evitar comportamientos extraños hay una regla a seguir:
- La ruta (directorio) debe terminar con ''/''. Si la ruta termina sin ''/'', la última parte se trata como un nombre de archivo y se concatenará cuando se intente combinar con la siguiente parte de la URL.
- Hay una excepción: la dirección URL base (sin información de directorio) no debe terminar con ''/''
- la parte de la ruta no debe comenzar con ''/''. Si comienza con ''/'', se elimina toda la información relativa existente de la URL ... agregando una
string.Empty
ruta de la partestring.Empty
eliminará también el directorio relativo de la URL!
Si sigue las reglas anteriores, puede combinar las URL con el código a continuación. Dependiendo de su situación, puede agregar múltiples partes de ''directorio'' a la URL ...
var pathParts = new string[] { destinationBaseUrl, destinationFolderUrl, fileName };
var destination = pathParts.Aggregate((left, right) =>
{
if (string.IsNullOrWhiteSpace(right))
return left;
return new Uri(new Uri(left), right).ToString();
});
Ambos de estos trabajos:
Uri final = new Uri(Regex.Replace(baseUrl + "/" + relativePath, "(?<!http:)/{2,}", "/"));
O
Uri final =new Uri(string.Format("{0}/{1}", baseUrl.ToString().TrimEnd(''/''), relativePath.ToString().TrimStart(''/'')));
Si si
baseUrl = "http://tesrurl.test.com/Int18"
y
relativePath = "To_Folder"
output = http://tesrurl.test.com/Int18/To_Folder
Algunos errores aparecerán para el siguiente código:
// If you use the below code, some issues will be there in the final URI
Uri final = new Uri(baseUrl, relativePath);
Usamos el siguiente método simple de ayuda para unir un número arbitrario de partes de URL:
public static string JoinUrlParts(params string[] urlParts)
{
return string.Join("/", urlParts.Where(up => !string.IsNullOrEmpty(up)).ToList().Select(up => up.Trim(''/'')).ToArray());
}
Tenga en cuenta que no admite las URL relativas al estilo ''../../something/page.htm''- style.
Encontré que el Uri
constructor voltea ''/' en ''/''. Así que también puedes usar Path.Combine
, con el Uri
constructor.
Uri baseUri = new Uri("http://MyUrl.com");
string path = Path.Combine("Images", "Image.jpg");
Uri myUri = new Uri(baseUri, path);
Path.Combine("Http://MyUrl.com/", "/Images/Image.jpg").Replace("//", "/")