Path.Combine per URL?


1244

Path.Combine è utile, ma esiste una funzione simile nel framework .NET per gli URL ?

Sto cercando una sintassi come questa:

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

che restituirebbe:

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


14
Flurl include un Url.Combinemetodo che fa proprio questo.
Todd Menier,

2
In realtà, // è gestito dal routing del sito Web o del server e non dal browser. Invierà ciò che hai inserito nella barra degli indirizzi. Ecco perché si verificano problemi quando si digita htp: // anziché http: // Quindi // può causare gravi problemi su alcuni siti. Sto scrivendo una DLL per un crawler che gestisce un particolare sito Web che genera un 404 se hai // nell'URL.
Dave Gordon,

Risposte:


73

C'è un commento di Todd Menier sopra che Flurl include a Url.Combine.

Più dettagli:

Url.Combine è fondamentalmente un Path.Combine per gli URL, garantendo uno e un solo carattere separatore tra le parti:

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" 

Ottieni Flurl.Http su NuGet :

PM> Installa-pacchetto Flurl. Http

Oppure ottieni il generatore di URL autonomo senza le funzionalità HTTP:

PM> Installa pacchetto Flurl


4
Bene, questa domanda riceve molto traffico e la risposta con oltre 1000 voti non funziona in tutti i casi. Anni dopo, utilizzo Flurl per questo, quindi accetto questo. Sembra funzionare in tutti i casi che ho riscontrato. Se le persone non vogliono prendere una dipendenza, ho pubblicato una risposta che funziona anche bene.
Brian MacKay,

e se non usi Flurle preferisci una versione leggera, github.com/jean-lourenco/UrlCombine
lizzy91

1157

Uri ha un costruttore che dovrebbe fare questo per te: new Uri(Uri baseUri, string relativeUri)

Ecco un esempio:

Uri baseUri = new Uri("http://www.contoso.com");
Uri myUri = new Uri(baseUri, "catalog/shownew.htm");

Nota dell'editor: attenzione, questo metodo non funziona come previsto. In alcuni casi può tagliare parte di BaseUri. Vedi commenti e altre risposte.


369
Mi piace l'uso della classe Uri, sfortunatamente non si comporterà come Path.Combine come richiesto dall'OP. Ad esempio new Uri (new Uri (" test.com/mydirectory/" ), "/helloworld.aspx"). ToString () ti dà " test.com/helloworld.aspx "; che sarebbe errato se volessimo un risultato in stile Path.Combine.
Doctor Jones,

195
È tutto nelle barre. Se la parte relativa del percorso inizia con una barra, si comporta come descritto. Ma, se lasci la barra, funziona come ti aspetteresti (nota la barra mancante sul secondo parametro): new Uri (new Uri (" test.com/mydirectory/" ), "helloworld.aspx" ). ToString () genera " test.com/mydirectory/helloworld.aspx ". Path.Combine si comporta in modo simile. Se il parametro relativo percorso inizia con una barra, restituisce solo il percorso relativo e non li combina.
Joel Beckham,

70
Se la tua baseUri fosse "test.com/mydirectory/mysubdirectory", il risultato sarebbe "test.com/mydirectory/helloworld.aspx" anziché "test.com/mydirectory/mysubdirectory/helloworld.aspx". La sottile differenza è la mancanza di barra finale sul primo parametro. Sono tutto per usare i metodi del framework esistente, se devo avere già la barra finale allora penso che fare partUrl1 + partUrl2 abbia un odore molto meno - avrei potuto potenzialmente inseguire quella barra finale per un bel po 'tutto per per non concedere stringhe.
Carl,

64
L'unico motivo per cui voglio un metodo di combinazione URI è che non devo controllare la barra finale. Request.ApplicationPath è '/' se l'applicazione è alla radice, ma '/ foo' se non lo è.
Nick

24
I -1 questa risposta perché non risponde al problema. Quando vuoi combinare l'URL, come quando vuoi usare Path.Combine, non vuoi preoccuparti del trailing /. e con questo, devi preoccuparti. Preferisco la soluzione di Brian MacKay o mdsharpe sopra
Baptiste Pernet,

161

Questa potrebbe essere una soluzione opportunamente semplice:

public static string Combine(string uri1, string uri2)
{
    uri1 = uri1.TrimEnd('/');
    uri2 = uri2.TrimStart('/');
    return string.Format("{0}/{1}", uri1, uri2);
}

7
+1: Sebbene questo non gestisca percorsi in stile relativo (../../whatever.html), questo mi piace per la sua semplicità. Aggiungerei anche i ritagli per il carattere "\".
Brian MacKay,

3
Vedi la mia risposta per una versione più completa di questo.
Brian MacKay,

149

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

Tornerà:

http://msdn.microsoft.com/en-us/library/system.uri.trycreate.aspx


53
+1: va bene, anche se ho un problema irrazionale con il parametro di output. ;)
Brian MacKay,

10
@Brian: se aiuta, tutti i metodi TryXXX ( int.TryParse, DateTime.TryParseExact) hanno questo parametro di output per rendere più semplice il loro utilizzo in un'istruzione if. A proposito, non è necessario inizializzare la variabile come ha fatto Ryan in questo esempio.
Abele,

41
Questa risposta soffre dello stesso problema di Joel : unirsi test.com/mydirectory/e /helloworld.aspxrisulterà in ciò test.com/helloworld.aspxche apparentemente non è quello che vuoi.
Matt Kocaj,

3
Salve, questo non è riuscito per quanto segue: if (Uri.TryCreate (new Uri (" localhost / MyService /" ), "/ Event / SomeMethod? Abc = 123", out result)) {Console.WriteLine (risultato); } Mi sta mostrando il risultato come: localhost / Event / SomeMethod? Abc = 123 Nota: "http: //" è sostituito da Uri di base qui da StackOverflow
Faisal Mq

3
@FaisalMq Questo è il comportamento corretto, poiché hai passato un secondo parametro relativo alla radice. Se avessi lasciato fuori il primo / sul secondo parametro, avresti ottenuto il risultato che ti aspettavi.
Tom Lint,

127

Ci sono già delle ottime risposte qui. Sulla base del suggerimento mdsharpe, ecco un metodo di estensione che può essere facilmente utilizzato quando si desidera gestire le istanze di 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('/'))));
    }
}

E esempio di utilizzo:

var url = new Uri("http://example.com/subpath/").Append("/part1/", "part2").AbsoluteUri;

Questo produrrà http://example.com/subpath/part1/part2


2
Questa soluzione rende banale scrivere un metodo statico UriUtils.Combine ("base url", "part1", "part2", ...) che è molto simile a Path.Combine (). Bello!
angularsen,

Per supportare gli URI relativi ho dovuto usare ToString () invece di AbsoluteUri e UriKind.AbsoluteOrRelative nel costruttore Uri.
angularsen,

Grazie per il suggerimento su Uris relativo. Sfortunatamente Uri non semplifica la gestione dei percorsi relativi in ​​quanto vi sono sempre problemi con Request.ApplicationPath. Forse potresti anche provare a usare il nuovo Uri (HttpContext.Current.Request.ApplicationPath) come base e chiamare Append su di esso? Questo ti darà percorsi assoluti ma dovrebbe funzionare ovunque all'interno della struttura del sito.
Ales Potocnik Hahonina,

Grande. Sono contento che abbia aiutato qualcun altro. Lo sto usando da qualche tempo e non ho avuto problemi.
Ales Potocnik Hahonina,

Ho anche aggiunto verificare se uno dei percorsi da aggiungere non è una stringa nulla o vuota.
n.podbielski,

92

La risposta di Ryan Cook è vicina a ciò che sto cercando e potrebbe essere più appropriata per altri sviluppatori. Tuttavia, aggiunge http: // all'inizio della stringa e in generale esegue un po 'più di formattazione di quello che sto cercando.

Inoltre, per i miei casi d'uso, la risoluzione dei percorsi relativi non è importante.

La risposta di mdsharp contiene anche il seme di una buona idea, sebbene quell'attuazione effettiva necessitasse di qualche dettaglio in più per essere completa. Questo è un tentativo di perfezionarlo (e lo sto usando in produzione):

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

Questo codice supera il seguente test, che si trova in 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

4
Parlando di dettagli: che dire dell'obbligo ArgumentNullException("url1")se l'argomento è Nothing? Scusa, solo essere schizzinoso ;-). Nota che una barra rovesciata non ha nulla a che fare con un URI (e se è presente, non deve essere ritagliata), quindi puoi rimuoverla da TrimXXX.
Abele,

4
puoi usare params string [] e unirti in modo ricorsivo per consentire più di 2 combinazioni
Jaider

4
Vorrei che questo fosse nella Biblioteca di classe base come Path.Combine.
Uriah Blatherwick,

1
@MarkHurd Ho modificato di nuovo il codice, in modo che sia comportamentalmente uguale al C # e sintatticamente equivalente.
JJS

1
@BrianMacKay l'ho rotto, markhurd ha sottolineato il mio errore e si è ritirato, ho aggiornato di nuovo ... evviva
JJS

36

Path.Combine non funziona per me perché ci possono essere caratteri come "|" negli argomenti QueryString e quindi nell'URL, che risulterà in ArgumentException.

Per prima cosa ho provato il nuovo Uri(Uri baseUri, string relativeUri)approccio, che per me è fallito a causa di URI come http://www.mediawiki.org/wiki/Special:SpecialPages:

new Uri(new Uri("http://www.mediawiki.org/wiki/"), "Special:SpecialPages")

si tradurrà in Special: SpecialPages, a causa dei due punti dopo Specialche indica uno schema.

Quindi alla fine ho dovuto prendere il percorso mdsharpe / Brian MacKays e svilupparlo ulteriormente per lavorare con più parti URI:

public static string CombineUri(params string[] uriParts)
{
    string uri = string.Empty;
    if (uriParts != null && uriParts.Length > 0)
    {
        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;
}

Uso: CombineUri("http://www.mediawiki.org/", "wiki", "Special:SpecialPages")


1
+1: Adesso stiamo parlando ... Lo proverò. Questo potrebbe persino finire per essere la nuova risposta accettata. Dopo aver provato il nuovo metodo Uri () non mi piace davvero. Troppo fine.
Brian MacKay,

Questo è esattamente quello di cui avevo bisogno! Non ero un fan del fatto che dovevo preoccuparmi di dove mettevo barre sottili, ecc ...
Gromer,

+1 per l'inserimento del controllo null in modo che non esploda.
NightOwl888

Count () dovrebbe essere Lunghezza in modo che non sia necessario includere Linq nella libreria solo per quello.
PRMan,

Questo era esattamente quello che stavo cercando.
ThePeter

34

In base all'URL di esempio che hai fornito, suppongo che desideri combinare gli URL relativi al tuo sito.

Sulla base di questo presupposto, proporrò questa soluzione come la risposta più appropriata alla tua domanda che era: "Path.Combine è utile, esiste una funzione simile nel framework per gli URL?"

Poiché esiste una funzione simile nel framework per gli URL, propongo che il metodo corretto sia: metodo "VirtualPathUtility.Combine". Ecco il link di riferimento MSDN: Metodo VirtualPathUtility.Combine

C'è un avvertimento: credo che questo funzioni solo per gli URL relativi al tuo sito (ovvero, non puoi usarlo per generare collegamenti a un altro sito Web. Ad esempio var url = VirtualPathUtility.Combine("www.google.com", "accounts/widgets");).


+1 perché è vicino a quello che sto cercando, anche se sarebbe l'ideale se funzionasse per qualsiasi vecchio URL. Raddoppierò diventerà molto più elegante di quanto proposto da mdsharpe.
Brian MacKay,

2
L'avvertenza è corretta, non può funzionare con uris assoluto e il risultato è sempre relativo dalla radice. Ma ha un ulteriore vantaggio, elabora la tilde, come con "~ /". Questo lo rende una scorciatoia Server.MapPathe una combinazione.
Abele,

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

12
path.Replace(Path.DirectorySeparatorChar, '/');
Jaider,

5
path.Replace(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar)
SliverNinja - MSFT,

1
Per farla crollare devi rimuovere il primo / nel secondo argomento, ad esempio "/ Immagini" - / Path.Combine (" Http://MyUrl.com ", "Immagini / Image.jpg")
Per G

8
@SliverNinja Non è corretto Il valore di questo campo è una barra rovesciata ('\') su UNIX e una barra ('/') su sistemi operativi Windows e Macintosh. Quando si utilizza Mono su un sistema Linux, si ottiene il separatore sbagliato.
user247702

6
Tutti voi che state cercando il separatore di directory stanno dimenticando che le stringhe potrebbero provenire da un sistema operativo diverso da quello in cui vi trovate ora. Basta sostituire la barra rovesciata con la barra in avanti e il gioco è fatto.
JeremyWeir,

17

Ho appena messo insieme un piccolo metodo di estensione:

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('/');
        }

Può essere usato in questo modo:

"www.example.com/".UriCombine("/images").UriCombine("first.jpeg");

12

Esempio spiritoso, Ryan, per finire con un link alla funzione. Molto bene.

Un consiglio Brian: se si avvolge questo codice in una funzione, è possibile utilizzare un UriBuilder per racchiudere l'URL di base prima della chiamata TryCreate.

Altrimenti, l'URL di base DEVE includere lo schema (in cui UriBuilder assumerà http: //). Solo un pensiero:

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

10

Un modo semplice per combinarli e assicurarsi che sia sempre corretto è:

string.Format("{0}/{1}", Url1.Trim('/'), Url2);

+1, sebbene sia molto simile alla risposta di mdsharpe, che ho migliorato nella mia risposta. Questa versione funziona alla grande a meno che Url2 non inizi con / o \, oppure Url1 finisca accidentalmente in \, o uno dei due sia vuoto! :)
Brian MacKay,

9

La combinazione di più parti di un URL potrebbe essere un po 'complicata. È possibile utilizzare il costruttore a due parametri Uri(baseUri, relativeUri)oppure è possibile utilizzare la Uri.TryCreate()funzione di utilità.

In entrambi i casi, potresti finire per restituire un risultato errato perché questi metodi continuano a troncare le parti relative fuori dal primo parametro baseUri, cioè da qualcosa di simile http://google.com/some/thinga http://google.com.

Per poter combinare più parti in un URL finale, è possibile copiare le due funzioni seguenti:

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

Il codice completo con unit test per dimostrare l'utilizzo è disponibile all'indirizzo https://uricombine.codeplex.com/SourceControl/latest#UriCombine/Uri.cs

Ho dei test unitari per coprire i tre casi più comuni:

Inserisci qui la descrizione dell'immagine


2
Mi sembra abbastanza buono. Sebbene sia possibile sostituire l'anello I con un ciclo foreach per una migliore chiarezza.
Chris Marisic,

Grazie Chris. Ho appena cambiato il mio codice per usare Foreach.
Believe2014,

1
+1 per tutto lo sforzo extra. Devo mantenere un po 'questa domanda per alcune delle risposte più votate, hai buttato giù il guanto. ;)
Brian MacKay,

Scusa, ma non abbastanza. Funziona nei pochi casi che mostri ma lungi dall'essere utilizzabile in tutte le combinazioni. Ad esempio, i due punti nel percorso causeranno danni.
Gábor,

Puoi per favore fare un esempio di cosa intendi? Sarò felice di risolvere il problema e aiutare i prossimi utenti.
Believe2014

7

Ho trovato UriBuilderfunzionato davvero bene per questo genere di cose:

UriBuilder urlb = new UriBuilder("http", _serverAddress, _webPort, _filePath);
Uri url = urlb.Uri;
return url.AbsoluteUri;

Vedi UriBuilder Class - MSDN per ulteriori costruttori e documentazione.


4

Ecco il metodo Microsoft (OfficeDev PnP) UrlUtility.Combine :

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

Fonte: GitHub


Sembra che questo potrebbe essere per i percorsi, piuttosto che per gli URL.
Brian MacKay,

@BrianMacKay Concordato sul fatto che sembra, ma appartiene alla classe UrlUtility e utilizzato nel contesto della combinazione di URL

2
Modificato per chiarire a quale classe appartiene

Fare attenzione quando si utilizza questa classe, il resto della classe contiene artefatti specifici di SharePoint.
Harry Berry,

4

Trovo quanto segue utile e ha le seguenti caratteristiche:

  • Genera spazio nullo o bianco
  • Accetta più paramsparametri per più segmenti di URL
  • viene impostato su null o vuoto

Classe

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

test

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

@PeterMortensen grazie per la modifica
TheGeneral

Alcuni problemi con i test: // Risultato = test1 / test2 / test3 \ per il 4 ° e l'ultimo dei test di lancio dà ArgumentNullException invece di ArgumentException
Moriya

3

La mia soluzione generica:

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

Questo metodo di supporto è molto flessibile e funziona bene in molti casi d'uso diversi. Grazie!
Shiva,

3

Ho creato questa funzione che ti semplificherà la vita:

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

Funziona con URL e percorsi normali.

Uso:

    // 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

3

Perché non usare solo quanto segue.

System.IO.Path.Combine(rootUrl, subPath).Replace(@"\", "/")

Cercavo la versione di PowerShell di questo che sarebbe: [System.IO.Path]::Combine("http://MyUrl.com/","/Images/Image.jpg")tuttavia questo non riesce con un risultato di: /Images/Image.jpg. Rimuovi il /secondo subPath e funziona:[System.IO.Path]::Combine("http://MyUrl.com/","Images/Image.jpg")
Underverse

Bella idea, ma non riesce, quando uno dei parametri è null.
pholpar il

2

Regole durante la combinazione di URL con un URI

Per evitare comportamenti strani c'è una regola da seguire:

  • Il percorso (directory) deve terminare con '/'. Se il percorso termina senza '/', l'ultima parte viene trattata come un nome file e verrà concatenata quando si tenta di combinare con la parte URL successiva.
  • C'è un'eccezione: l'indirizzo URL di base (senza informazioni sulla directory) non deve terminare con '/'
  • la parte del percorso non deve iniziare con '/'. Se inizia con '/', tutte le informazioni relative esistenti dall'URL vengono eliminate ... aggiungendo astring.Empty percorso parte rimuoverà anche la relativa directory dall'URL!

Se segui le regole precedenti, puoi combinare gli URL con il codice seguente. A seconda della situazione, è possibile aggiungere più parti 'directory' all'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();
        });

2

Se non si desidera aggiungere una dipendenza di terze parti come Flurl o creare un metodo di estensione personalizzato, in ASP.NET Core (disponibile anche in Microsoft.Owin), è possibile utilizzare quello PathStringdestinato allo scopo di creare URI percorsi. È quindi possibile creare l'URI completo utilizzando una combinazione di questo Urie UriBuilder.

In questo caso, sarebbe:

new Uri(new UriBuilder("http", "MyUrl.com").Uri, new PathString("/Images").Add("/Image.jpg").ToString())

Questo ti dà tutte le parti costituenti senza dover specificare i separatori nell'URL di base. Sfortunatamente, PathStringrichiede che /sia anteposto a ciascuna stringa, altrimenti in effetti genera un ArgumentException! Ma almeno puoi costruire il tuo URI in modo deterministico in un modo che sia facilmente testabile in unità.


2

Quindi ho un altro approccio, simile a tutti quelli che hanno usato UriBuilder.

Non volevo dividere BaseUrl (che può contenere una parte del percorso, ad esempio http://mybaseurl.com/dev/ ) come faceva javajavajavajavajava .

Il frammento seguente mostra il codice + Test.

Attenzione: questa soluzione mette in minuscolo l'host e aggiunge una porta. Se ciò non è desiderato, è possibile scrivere una rappresentazione in forma di stringa, ad esempio sfruttando la Uriproprietà di UriBuilder.

  public class Tests
  {
         public static string CombineUrl (string baseUrl, string path)
         {
           var uriBuilder = new UriBuilder (baseUrl);
           uriBuilder.Path = Path.Combine (uriBuilder.Path, path);
           return uriBuilder.ToString();
         }

         [TestCase("http://MyUrl.com/", "/Images/Image.jpg", "http://myurl.com:80/Images/Image.jpg")]
         [TestCase("http://MyUrl.com/basePath", "/Images/Image.jpg", "http://myurl.com:80/Images/Image.jpg")]
         [TestCase("http://MyUrl.com/basePath", "Images/Image.jpg", "http://myurl.com:80/basePath/Images/Image.jpg")]
         [TestCase("http://MyUrl.com/basePath/", "Images/Image.jpg", "http://myurl.com:80/basePath/Images/Image.jpg")]
         public void Test1 (string baseUrl, string path, string expected)
         {
           var result = CombineUrl (baseUrl, path);

           Assert.That (result, Is.EqualTo (expected));
         }
  }

Testato con .NET Core 2.1 su Windows 10.

Perché funziona?

Anche se Path.Combinerestituirà Backslash (su Windows almeno), UriBuilder gestisce questo caso nel Setter di Path.

Tratto da https://github.com/dotnet/corefx/blob/master/src/System.Private.Uri/src/System/UriBuilder.cs (attenzione alla chiamata a string.Replace)

[AllowNull]
public string Path
{
      get
      {
          return _path;
      }
      set
      {
          if ((value == null) || (value.Length == 0))
          {
              value = "/";
          }
          _path = Uri.InternalEscapeString(value.Replace('\\', '/'));
          _changed = true;
      }
 }

È questo l'approccio migliore?

Certamente questa soluzione è abbastanza auto-descrittiva (almeno secondo me). Ma stai facendo affidamento su "funzionalità" non documentate (almeno non ho trovato nulla con una rapida ricerca su Google) dall'API .NET. Questo potrebbe cambiare con una versione futura, quindi ti preghiamo di coprire il metodo con i test.

Ci sono test in https://github.com/dotnet/corefx/blob/master/src/System.Private.Uri/tests/FunctionalTests/UriBuilderTests.cs ( Path_Get_Set) che controlla, se \è stato correttamente trasformato.

Nota a margine: si potrebbe anche lavorare UriBuilder.Uridirettamente con la proprietà, se l'uri verrà utilizzato per un System.Urictor.


Questo è un approccio molto affidabile. Complimenti per il test unitario !!
aggsol,

2

Per chiunque cerchi un one-liner e desideri semplicemente unire parti di un percorso senza creare un nuovo metodo o fare riferimento a una nuova libreria o costruire un valore URI e convertirlo in una stringa, quindi ...

string urlToImage = String.Join("/", "websiteUrl", "folder1", "folder2", "folder3", "item");

È piuttosto semplice, ma non vedo di cosa hai bisogno. Se hai paura di raddoppiare '/', allora puoi semplicemente fare un .Replace("//", "/")dopo. Se hai paura di sostituire il "//" raddoppiato in "https: //", esegui invece un join, sostituisci il "/" raddoppiato, quindi unisciti all'URL del sito Web (tuttavia sono abbastanza sicuro che la maggior parte dei browser lo farà automaticamente converti qualsiasi cosa con 'https:' nella parte frontale per leggere nel formato corretto). Questo sembrerebbe:

string urlToImage = String.Join("/","websiteUrl", String.Join("/", "folder1", "folder2", "folder3", "item").Replace("//","/"));

Ci sono molte risposte qui che gestiranno tutto quanto sopra, ma nel mio caso, ne avevo bisogno solo una volta in una posizione e non avrò bisogno di fare molto affidamento su di esso. Inoltre, è davvero facile vedere cosa sta succedendo qui.

Vedi: https://docs.microsoft.com/en-us/dotnet/api/system.string.join?view=netframework-4.8


1

Uso:

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

Ha il vantaggio di comportarsi esattamente come Path.Combine.


1

Ecco il mio approccio e lo userò anche per me stesso:

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

Questo è accettabile solo per il tuo caso. Ci sono casi che potrebbero violare il tuo codice. Inoltre, non hai eseguito la codifica corretta delle parti del percorso. Questa potrebbe essere un'enorme vulnerabilità quando si tratta di attacchi di cross-site scripting.
Believe2014,

Accetto i tuoi punti. Il codice dovrebbe fare semplicemente la combinazione di due parti dell'URL.
Amit Bhagat,

1

Usa questo:

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

Bel tocco con 'WebPath'. :) Il codice potrebbe essere inutilmente denso però - è difficile per me dare un'occhiata a questo e dire, sì, è perfetto. Mi fa venire voglia di vedere i test unitari. Forse sono solo io!
Brian MacKay,

1
x.StartsWith ("/") &&! x.StartsWith ("http") - perché il controllo http? cosa guadagni?
penguat,

Non vuoi provare a eliminare la barra se inizia con http.
Martin Murphy,

@BrianMacKay, non sono sicuro che un due liner garantisca un test unitario, ma se ti piace sentiti libero di fornirne uno. Non è come se stessi accettando patch o altro, ma sentiti libero di modificare il suggerimento.
Martin Murphy,

1

Ho scoperto che il Uricostruttore lancia "\" in "/". Quindi puoi anche usare Path.Combine, con il Uricostruttore.

 Uri baseUri = new Uri("http://MyUrl.com");
 string path = Path.Combine("Images", "Image.jpg");
 Uri myUri = new Uri(baseUri, path);

1

Per quello che vale, ecco un paio di metodi di estensione. Il primo combina i percorsi e il secondo aggiunge i parametri all'URL.

    public static string CombineUrl(this string root, string path, params string[] paths)
    {
        if (string.IsNullOrWhiteSpace(path))
        {
            return root;
        }

        Uri baseUri = new Uri(root);
        Uri combinedPaths = new Uri(baseUri, path);

        foreach (string extendedPath in paths)
        {
           combinedPaths = new Uri(combinedPaths, extendedPath);
        }

        return combinedPaths.AbsoluteUri;
    }

    public static string AddUrlParams(this string url, Dictionary<string, string> parameters)
    {
        if (parameters == null || !parameters.Keys.Any())
        {
            return url;
        }

        var tempUrl = new StringBuilder($"{url}?");
        int count = 0;

        foreach (KeyValuePair<string, string> parameter in parameters)
        {
            if (count > 0)
            {
                tempUrl.Append("&");
            }

            tempUrl.Append($"{WebUtility.UrlEncode(parameter.Key)}={WebUtility.UrlEncode(parameter.Value)}");
            count++;
        }

        return tempUrl.ToString();
    }

1

Come si trova in altre risposte, nuove Uri()o TryCreate()possono fare il segno di spunta. Tuttavia, la base Uri deve finire /e il parente NON deve iniziare /; in caso contrario rimuoverà la parte finale dell'URL di base

Penso che questo sia fatto meglio come metodo di estensione, cioè

public static Uri Append(this Uri uri, string relativePath)
{
    var baseUri = uri.AbsoluteUri.EndsWith('/') ? uri : new Uri(uri.AbsoluteUri + '/');
    var relative = relativePath.StartsWith('/') ? relativePath.Substring(1) : relativePath;
    return new Uri(baseUri, relative);
}

e per usarlo:

var baseUri = new Uri("http://test.com/test/");
var combinedUri =  baseUri.Append("/Do/Something");

In termini di prestazioni, ciò consuma più risorse del necessario, a causa della classe Uri che esegue molte analisi e convalide; una profilatura molto approssimativa (Debug) ha fatto un milione di operazioni in circa 2 secondi. Funzionerà per la maggior parte degli scenari, tuttavia per essere più efficiente, è meglio manipolare tutto come stringhe, ciò richiede 125 millisecondi per 1 milione di operazioni. ie

public static string Append(this Uri uri, string relativePath)
{
    //avoid the use of Uri as it's not needed, and adds a bit of overhead.
    var absoluteUri = uri.AbsoluteUri; //a calculated property, better cache it
    var baseUri = absoluteUri.EndsWith('/') ? absoluteUri : absoluteUri + '/';
    var relative = relativePath.StartsWith('/') ? relativePath.Substring(1) : relativePath;
    return baseUri + relative;
}

E se desideri comunque restituire un URI, sono necessari circa 600 millisecondi per 1 milione di operazioni.

public static Uri AppendUri(this Uri uri, string relativePath)
{
    //avoid the use of Uri as it's not needed, and adds a bit of overhead.
    var absoluteUri = uri.AbsoluteUri; //a calculated property, better cache it
    var baseUri = absoluteUri.EndsWith('/') ? absoluteUri : absoluteUri + '/';
    var relative = relativePath.StartsWith('/') ? relativePath.Substring(1) : relativePath;
    return new Uri(baseUri + relative);
}

Spero che questo possa essere d'aiuto.


1

Penso che questo dovrebbe darti una maggiore flessibilità in quanto puoi gestire tutti i segmenti di percorso che desideri:

public static string UrlCombine(this string baseUrl, params string[] segments)
=> string.Join("/", new[] { baseUrl.TrimEnd('/') }.Concat(segments.Select(s => s.Trim('/'))));
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.