Path.Combine untuk URL?

1244

Path.Combine berguna, tetapi apakah ada fungsi yang serupa dalam kerangka .NET untuk URL ?

Saya mencari sintaks seperti ini:

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

yang akan kembali:

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

Brian MacKay
sumber
14
Flurl termasuk Url.Combinemetode yang melakukan hal itu.
Todd Menier
2
Sebenarnya, // ditangani oleh perutean situs web atau server dan bukan oleh peramban. Ini akan mengirim apa yang Anda masukkan ke bilah alamat. Itu sebabnya kami mendapat masalah saat kami mengetik htp: // alih-alih http: // Jadi // dapat menyebabkan masalah besar pada beberapa situs. Saya menulis .dll untuk perayap yang menangani situs web tertentu yang melempar 404 jika Anda memiliki // di url.
Dave Gordon

Jawaban:

73

Ada komentar Todd Menier di atas bahwa Flurl termasuk a Url.Combine.

Keterangan lebih lanjut:

Url.Combine pada dasarnya adalah Path.Combine untuk URL, memastikan satu dan hanya satu karakter pemisah antara bagian:

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" 

Dapatkan Flurl.Http di NuGet :

PM> Instal-Paket Flurl.Http

Atau dapatkan pembuat URL yang berdiri sendiri tanpa fitur HTTP:

PM> Paket Instalasi Flurl

Michael Freidgeim
sumber
4
Nah, pertanyaan ini mendapat banyak lalu lintas, dan jawaban dengan 1000+ upvotes sebenarnya tidak berfungsi dalam semua kasus. Bertahun-tahun kemudian, saya benar-benar menggunakan Flurl untuk ini, jadi saya menerima yang ini. Tampaknya berfungsi dalam semua kasus yang saya temui. Jika orang tidak ingin mengambil ketergantungan, saya memposting jawaban yang juga berfungsi dengan baik.
Brian MacKay
dan jika Anda tidak menggunakan Flurldan akan membatalkan
lizzy91
1157

Uri memiliki konstruktor yang harus melakukan ini untuk Anda: new Uri(Uri baseUri, string relativeUri)

Ini sebuah contoh:

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

Catatan dari editor: Hati-hati, metode ini tidak berfungsi seperti yang diharapkan. Itu dapat memotong bagian baseUri dalam beberapa kasus. Lihat komentar dan jawaban lain.

Joel Beckham
sumber
369
Saya suka menggunakan kelas Uri, sayangnya itu tidak akan berperilaku seperti Path.Combine seperti yang diminta OP. Misalnya Uri baru (Uri baru (" test.com/mydirectory/" ), "/helloworld.aspx"). ToString () memberi Anda " test.com/helloworld.aspx "; yang akan salah jika kita menginginkan hasil gaya Path.Combine.
Dokter Jones
195
Semuanya ada di garis miring. Jika bagian jalur relatif dimulai dengan garis miring, maka itu berperilaku seperti yang Anda jelaskan. Tetapi, jika Anda membiarkan slash out, maka itu berfungsi seperti yang Anda harapkan (perhatikan slash yang hilang pada parameter kedua): Uri baru (Uri baru (" test.com/mydirectory/" ), "helloworld.aspx" ) .ToString () menghasilkan " test.com/mydirectory/helloworld.aspx ". Path.Combine berperilaku sama. Jika parameter jalur relatif dimulai dengan garis miring, ia hanya mengembalikan jalur relatif dan tidak menggabungkannya.
Joel Beckham
70
Jika baseUri Anda adalah "test.com/mydirectory/mysubdirectory" maka hasilnya adalah "test.com/mydirectory/helloworld.aspx" alih-alih "test.com/mydirectory/mysubdirectory/helloworld.aspx". Perbedaan yang halus adalah kurangnya trailing slash pada parameter pertama. Saya semua menggunakan metode kerangka kerja yang ada, jika saya harus memiliki trailing slash di sana maka saya pikir melakukan partUrl1 + partUrl2 jauh lebih sedikit - saya bisa berpotensi mengejar trailing slash round cukup lama untuk Demi tidak melakukan string concat.
Carl
64
Satu-satunya alasan saya ingin metode gabungan URI adalah agar saya tidak perlu memeriksa slash tambahan. Request.ApplicationPath adalah '/' jika aplikasi Anda di root, tetapi '/ foo' jika tidak.
julukan
24
Saya -1 jawaban ini karena ini tidak menjawab masalah. Ketika Anda ingin menggabungkan url, seperti ketika Anda ingin menggunakan Path.Combine, Anda tidak ingin peduli dengan trailing /. dan dengan ini, kamu harus peduli. Saya lebih suka solusi dari Brian MacKay atau mdsharpe di atas
Baptiste Pernet
161

Ini mungkin solusi sederhana yang sesuai:

public static string Combine(string uri1, string uri2)
{
    uri1 = uri1.TrimEnd('/');
    uri2 = uri2.TrimStart('/');
    return string.Format("{0}/{1}", uri1, uri2);
}
Matthew Sharpe
sumber
7
+1: Meskipun ini tidak menangani jalur relatif-gaya (../../wh whatever.html), saya suka yang ini karena kesederhanaannya. Saya juga akan menambahkan trim untuk karakter '\'.
Brian MacKay
3
Lihat jawaban saya untuk versi yang lebih lengkap dari ini.
Brian MacKay
149

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

Akan kembali:

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

Ryan Cook
sumber
53
+1: Ini bagus, meskipun saya memiliki masalah irasional dengan parameter output. ;)
Brian MacKay
10
@Brian: jika ini membantu, semua metode TryXXX ( int.TryParse, DateTime.TryParseExact) memiliki param output ini untuk membuatnya lebih mudah untuk menggunakannya dalam pernyataan-if. Btw, Anda tidak perlu menginisialisasi variabel seperti yang dilakukan Ryan dalam contoh ini.
Abel
41
Jawaban ini mengalami masalah yang sama dengan jawaban Joel : bergabung test.com/mydirectory/dan /helloworld.aspxakan menghasilkan test.com/helloworld.aspxyang tampaknya bukan yang Anda inginkan.
Matt Kocaj
3
Hai, ini gagal untuk mengikuti: if (Uri.TryCreate (Uri baru (" localhost / MyService /" ), "/ Event / SomeMethod? Abc = 123", hasil keluar)) {Console.WriteLine (hasil); } Ini menunjukkan kepada saya hasil sebagai: localhost / Event / SomeMethod? Abc = 123 Catatan: "http: //" diganti dari basis Uri di sini oleh stackoverflow
Faisal Mq
3
@FaisalMq Ini adalah perilaku yang benar, karena Anda melewati parameter kedua relatif-root. Jika Anda mengabaikan parameter leading / pada parameter kedua, Anda akan mendapatkan hasil yang Anda harapkan.
Tom Lint
127

Sudah ada beberapa jawaban bagus di sini. Berdasarkan saran mdsharpe, berikut ini adalah metode ekstensi yang dapat dengan mudah digunakan ketika Anda ingin berurusan dengan instance 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('/'))));
    }
}

Dan contoh penggunaan:

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

Ini akan menghasilkan http://example.com/subpath/part1/part2

Ales Potocnik Hahonina
sumber
2
Solusi ini membuatnya sepele untuk menulis UriUtils.Combine ("base url", "part1", "part2", ...) metode statis yang sangat mirip dengan Path.Combine (). Bagus!
angularsen
Untuk mendukung URI relatif, saya harus menggunakan ToString () alih-alih AbsoluteUri dan UriKind.AbsoluteOrRelative dalam konstruktor Uri.
angularsen
Terima kasih atas tip tentang Uris relatif. Sayangnya Uri tidak membuatnya mudah untuk berurusan dengan jalur relatif karena selalu ada beberapa mucking tentang dengan Request.ApplicationPath terlibat. Mungkin Anda juga bisa mencoba menggunakan Uri baru (HttpContext.Current.Request.ApplicationPath) sebagai basis dan cukup panggil Tambahkan di atasnya? Ini akan memberi Anda jalur absolut tetapi harus bekerja di mana saja dalam struktur situs.
Ales Potocnik Hahonina
Bagus. Senang itu membantu orang lain. Sudah menggunakan ini untuk beberapa waktu sekarang dan belum memiliki masalah.
Ales Potocnik Hahonina
Saya juga menambahkan memeriksa apakah ada jalan untuk menambahkan bukan null atau string kosong.
n.podbielski
92

Jawaban Ryan Cook dekat dengan apa yang saya cari dan mungkin lebih cocok untuk pengembang lain. Namun, ia menambahkan http: // ke awal string dan secara umum ia melakukan sedikit lebih banyak format daripada yang saya kejar.

Juga, untuk kasus penggunaan saya, menyelesaikan jalur relatif tidak penting.

Jawaban mdsharp juga mengandung benih ide yang bagus, meskipun implementasi aktual membutuhkan beberapa detail lagi untuk lengkap. Ini adalah upaya untuk menyempurnakannya (dan saya menggunakan ini dalam produksi):

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

Kode ini lolos tes berikut, yang kebetulan berada di 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
Brian MacKay
sumber
4
Berbicara tentang perincian: bagaimana dengan wajib ArgumentNullException("url1")jika argumennya Nothing? Maaf, hanya pilih-pilih ;-). Perhatikan bahwa backslash tidak ada hubungannya di URI (dan jika ada, tidak boleh dipangkas), sehingga Anda dapat menghapusnya dari TrimXXX Anda.
Abel
4
Anda dapat menggunakan string params [] dan bergabung secara rekursif untuk memungkinkan lebih dari 2 kombinasi
Jaider
4
Saya yakin berharap ini ada di Perpustakaan Kelas Dasar seperti Path.Combine.
Uriah Blatherwick
1
@ MarkHurd saya mengedit kode lagi, sehingga secara perilaku sama dengan C #, dan secara sintaksis juga sama.
JJS
1
@BrianMacKay saya memecahkannya, markhurd menunjukkan kesalahan saya dan memutar kembali, saya memperbarui lagi ... tepuk tangan
JJS
36

Path.Combine tidak berfungsi untuk saya karena mungkin ada karakter seperti "|" dalam argumen QueryString dan karenanya URL, yang akan menghasilkan ArgumentException.

Saya pertama kali mencoba Uri(Uri baseUri, string relativeUri)pendekatan baru , yang gagal bagi saya karena URI seperti http://www.mediawiki.org/wiki/Special:SpecialPages:

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

akan menghasilkan Special: SpecialPages, karena titik dua setelah Specialitu menunjukkan skema.

Jadi saya akhirnya harus mengambil rute mdsharpe / Brian MacKays dan mengembangkannya sedikit lebih jauh untuk bekerja dengan beberapa bagian 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;
}

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

Mike Fuchs
sumber
1
+1: Sekarang kita berbicara ... Saya akan mencoba ini. Ini bahkan mungkin berakhir dengan jawaban yang baru diterima. Setelah mencoba metode Uri () baru, saya benar-benar tidak menyukainya. Terlalu licik.
Brian MacKay
Inilah yang saya butuhkan! Bukan penggemar harus peduli di mana saya meletakkan garis miring, dll ...
Gromer
+1 untuk bergulir di pemeriksaan nol agar tidak meledak.
NightOwl888
Count () harus Panjang sehingga Anda tidak perlu menyertakan Linq di perpustakaan Anda hanya untuk itu.
PRMan
Ini persis apa yang saya cari.
ThePeter
34

Berdasarkan pada URL contoh Anda berikan, saya akan menganggap Anda ingin menggabungkan URL yang relatif dengan situs Anda.

Berdasarkan asumsi ini saya akan mengusulkan solusi ini sebagai jawaban yang paling tepat untuk pertanyaan Anda yaitu: "Path.Combine berguna, apakah ada fungsi yang sama dalam kerangka kerja untuk URL?"

Karena ada fungsi serupa dalam kerangka kerja untuk URL yang saya usulkan yang benar adalah: "VirtualPathUtility.Combine" metode. Berikut tautan referensi MSDN: Metode VirtualPathUtility.Combine

Ada satu peringatan: Saya percaya ini hanya berfungsi untuk URL relatif ke situs Anda (yaitu, Anda tidak dapat menggunakannya untuk menghasilkan tautan ke situs web lain. Misalnya, var url = VirtualPathUtility.Combine("www.google.com", "accounts/widgets");).

Jeronimo Colon III
sumber
Memberi +1 karena dekat dengan apa yang saya cari, meskipun itu akan ideal jika itu akan berfungsi untuk url lama apa pun. Saya menggandakannya akan mendapatkan jauh lebih elegan dari apa yang diusulkan mdsharpe.
Brian MacKay
2
Peringatan itu benar, tidak dapat bekerja dengan uris absolut dan hasilnya selalu relatif dari akar. Tetapi memiliki manfaat tambahan, memproses tilde, seperti dengan "~ /". Ini membuatnya menjadi jalan pintas untuk Server.MapPathdan menggabungkan.
Abel
25
Path.Combine("Http://MyUrl.com/", "/Images/Image.jpg").Replace("\\", "/")
JeremyWeir
sumber
12
path.Replace(Path.DirectorySeparatorChar, '/');
Jaider
5
path.Replace(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar)
SliverNinja - MSFT
1
Untuk mendapatkannya, Anda harus menghapus argumen pertama / dalam kedua yaitu "/ Gambar" - / Path.Combine (" Http://MyUrl.com ", "Images / Image.jpg")
Per G
8
@SliverNinja Itu tidak benar Nilai bidang ini adalah garis miring terbalik ('\') pada UNIX, dan garis miring ('/') pada sistem operasi Windows dan Macintosh. Saat menggunakan Mono di sistem Linux, Anda akan mendapatkan separator yang salah.
user247702
6
Semua kalian yang geeking di Pemisah Direktori lupa bahwa string bisa berasal dari OS yang berbeda dari yang ada sekarang. Cukup ganti backslash dengan garis miring dan Anda tertutup.
JeremyWeir
17

Saya hanya mengumpulkan metode ekstensi kecil:

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

Dapat digunakan seperti ini:

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

Contoh cerdas, Ryan, untuk mengakhiri dengan tautan ke fungsi. Sudah selesai dilakukan dengan baik.

Satu rekomendasi Brian: jika Anda membungkus kode ini dalam suatu fungsi, Anda mungkin ingin menggunakan UriBuilder untuk membungkus URL dasar sebelum panggilan TryCreate.

Jika tidak, URL dasar HARUS menyertakan skema (di mana UriBuilder akan menganggap http: //). Hanya pemikiran saja:

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

Cara mudah untuk menggabungkannya dan memastikan selalu benar adalah:

string.Format("{0}/{1}", Url1.Trim('/'), Url2);
Alex
sumber
+1, meskipun ini sangat mirip dengan jawaban mdsharpe, yang saya tingkatkan dalam jawaban saya. Versi ini berfungsi dengan baik kecuali Url2 dimulai dengan / atau \, atau Url1 secara tidak sengaja berakhir pada \, atau salah satunya kosong! :)
Brian MacKay
9

Menggabungkan beberapa bagian dari URL bisa sedikit rumit. Anda dapat menggunakan konstruktor dua parameter Uri(baseUri, relativeUri), atau Anda dapat menggunakan Uri.TryCreate()fungsi utilitas.

Dalam kedua kasus, Anda mungkin berakhir kembali hasil yang salah karena metode ini terus truncating bagian relatif off dari parameter pertama baseUri, yaitu dari sesuatu seperti http://google.com/some/thinguntuk http://google.com.

Agar dapat menggabungkan beberapa bagian menjadi URL akhir, Anda dapat menyalin dua fungsi di bawah ini:

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

Kode lengkap dengan tes unit untuk menunjukkan penggunaan dapat ditemukan di https://uricombine.codeplex.com/SourceControl/latest#UriCombine/Uri.cs

Saya memiliki unit test untuk mencakup tiga kasus paling umum:

Masukkan deskripsi gambar di sini

Believe2014
sumber
2
Terlihat bagus untukku. Meskipun Anda bisa mengganti I loop dengan loop foreach untuk kejelasan yang lebih baik.
Chris Marisic
Terimakasih Chris. Saya baru saja mengubah kode saya untuk menggunakan Foreach.
Believe2014
1
+1 untuk semua upaya ekstra. Saya perlu mempertahankan pertanyaan ini sedikit untuk beberapa jawaban yang lebih tinggi, Anda telah melemparkan tantangan. ;)
Brian MacKay
Maaf, tapi tidak cukup. Berfungsi dalam beberapa kasus yang Anda tunjukkan tetapi tidak dapat digunakan dalam kombinasi al. Misalnya, titik dua di jalan akan menyebabkan kerusakan.
Gábor
Bisakah Anda memberi contoh tentang apa yang Anda maksud? Saya akan dengan senang hati memperbaiki masalah ini dan membantu pengguna berikutnya.
Believe2014
7

Saya menemukan UriBuilderbekerja dengan sangat baik untuk hal semacam ini:

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

Lihat Kelas UriBuilder - MSDN untuk lebih banyak konstruktor dan dokumentasi.

javajavajavajavajava
sumber
4

Berikut ini UrlUtility.Combine metode 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);
    }

Sumber: GitHub

Chris Marisic
sumber
Sepertinya ini mungkin untuk jalur, bukan URL.
Brian MacKay
@BrianMacKay Setuju bahwa itu terlihat seperti itu, tetapi berasal dari kelas UrlUtility dan digunakan dalam konteks menggabungkan URL
2
Diedit untuk memperjelas kelasnya
Hati-hati saat menggunakan Kelas ini, seluruh kelas berisi artefak khusus SharePoint.
Harry Berry
4

Saya menemukan hal-hal berikut yang bermanfaat dan memiliki fitur-fitur berikut:

  • Membuang ruang nol atau putih
  • Mengambil beberapa paramsparameter untuk beberapa segmen Url
  • melempar pada nol atau kosong

Kelas

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

Tes

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
Umum
sumber
@PeterMortensen terima kasih untuk hasil edit
TheGeneral
Beberapa masalah dengan tes: // Hasil = test1 / test2 / test3 \ untuk yang ke-4 dan yang terakhir dari tes melempar memberikan ArgumentNullException bukannya ArgumentException
Moriya
3

Solusi umum saya:

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;
}
Alex Titarenko
sumber
Metode pembantu ini sangat fleksibel dan bekerja dengan baik dalam banyak kasus penggunaan yang berbeda. Terima kasih!
Shiva
3

Saya membuat fungsi ini yang akan membuat hidup Anda lebih mudah:

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

Ini berfungsi untuk URL dan juga jalur normal.

Pemakaian:

    // 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
bigworld12
sumber
3

Mengapa tidak menggunakan saja yang berikut ini.

System.IO.Path.Combine(rootUrl, subPath).Replace(@"\", "/")
Andreas
sumber
Saya sedang mencari versi PowerShell ini yang akan menjadi: [System.IO.Path]::Combine("http://MyUrl.com/","/Images/Image.jpg")namun ini gagal dengan hasil: /Images/Image.jpg. Hapus /dari subPath kedua dan berfungsi:[System.IO.Path]::Combine("http://MyUrl.com/","Images/Image.jpg")
Underverse
Ide bagus, tetapi gagal, ketika salah satu parameternya nol.
pholpar
2

Aturan saat menggabungkan URL dengan URI

Untuk menghindari perilaku aneh ada satu aturan yang harus diikuti:

  • Path (direktori) harus diakhiri dengan '/'. Jika path berakhir tanpa '/', bagian terakhir diperlakukan seperti nama file, dan itu akan disatukan ketika mencoba untuk menggabungkan dengan bagian URL berikutnya.
  • Ada satu pengecualian: alamat URL dasar (tanpa info direktori) tidak perlu diakhiri dengan '/'
  • bagian jalur tidak boleh dimulai dengan '/'. Jika dimulai dengan '/', setiap informasi relatif yang ada dari URL dihapus ... menambahkan string.Emptyjalur bagian juga akan menghapus direktori relatif dari URL!

Jika Anda mengikuti aturan di atas, Anda dapat menggabungkan URL dengan kode di bawah ini. Bergantung pada situasi Anda, Anda dapat menambahkan beberapa bagian 'direktori' ke 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();
        });
baHI
sumber
2

Jika Anda tidak ingin menambahkan ketergantungan pihak ketiga seperti Flurl atau membuat metode ekstensi kustom, di ASP.NET Core (juga tersedia di Microsoft.Owin), Anda dapat menggunakan PathStringyang dimaksudkan untuk tujuan membangun URI jalan. Anda kemudian dapat membuat URI lengkap menggunakan kombinasi ini, Uridan UriBuilder.

Dalam hal ini, itu akan menjadi:

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

Ini memberi Anda semua bagian konstituen tanpa harus menentukan pemisah di URL dasar. Sayangnya, PathStringmengharuskan yang /diawali dengan masing-masing string kalau tidak itu sebenarnya melempar ArgumentException! Tetapi setidaknya Anda dapat membangun URI Anda secara deterministik dengan cara yang mudah diuji unit.

Neo
sumber
2

Jadi saya punya pendekatan lain, mirip dengan semua orang yang menggunakan UriBuilder.

Saya tidak ingin membagi BaseUrl saya (yang dapat berisi bagian dari jalan - misalnya http://mybaseurl.com/dev/ ) seperti yang dilakukan javajavajavajavajavajava .

Cuplikan berikut menunjukkan kode + Tes.

Hati-hati: Solusi ini menurunkan host dan menambahkan port. Jika ini tidak diinginkan, seseorang dapat menulis representasi string dengan misalnya memanfaatkan UriProperti 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));
         }
  }

Diuji dengan .NET Core 2.1 pada Windows 10.

Mengapa ini bekerja?

Meskipun Path.Combineakan mengembalikan garis miring terbalik (pada Windows minimal), UriBuilder menangani kasus ini di Setter of Path.

Diambil dari https://github.com/dotnet/corefx/blob/master/src/System.Private.Uri/src/System/UriBuilder.cs (ingat panggilan untuk string.Replace)

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

Apakah ini pendekatan terbaik?

Tentu saja solusi ini cukup menggambarkan diri (setidaknya menurut saya). Tetapi Anda mengandalkan "fitur" tidak berdokumen (setidaknya saya tidak menemukan apa-apa dengan pencarian google cepat) dari. NET API. Ini dapat berubah dengan rilis di masa mendatang jadi harap tutupi Metode dengan Tes.

Ada tes di https://github.com/dotnet/corefx/blob/master/src/System.Private.Uri/tests/FunctionalTests/UriBuilderTests.cs ( Path_Get_Set) yang memeriksa, jika diperiksa dengan \benar.

Catatan Sisi: Seseorang juga dapat bekerja dengan UriBuilder.Uriproperti secara langsung, jika uri akan digunakan untuk System.Uriaktor.

Tobias Schwarzinger
sumber
Ini adalah pendekatan yang sangat andal. Acungan jempol untuk unit test !!
aggsol
2

Bagi siapa saja yang mencari satu-liner dan hanya ingin bergabung dengan bagian dari jalan tanpa membuat metode baru atau referensi perpustakaan baru atau membangun nilai URI dan mengonversinya menjadi string, maka ...

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

Ini cukup mendasar, tetapi saya tidak melihat apa lagi yang Anda butuhkan. Jika Anda takut dua kali lipat '/' maka Anda bisa melakukan .Replace("//", "/")sesudahnya. Jika Anda takut mengganti '//' yang dilipatgandakan dalam 'https: //', alih-alih lakukan satu bergabung, ganti '/' yang digandakan, kemudian bergabunglah dengan url situs web (namun saya cukup yakin sebagian besar browser akan secara otomatis konversikan apa pun dengan 'https:' di depannya untuk membaca dalam format yang benar). Ini akan terlihat seperti:

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

Ada banyak jawaban di sini yang akan menangani semua hal di atas, tetapi dalam kasus saya, saya hanya membutuhkannya sekali di satu lokasi dan tidak perlu terlalu bergantung padanya. Juga, sangat mudah untuk melihat apa yang sedang terjadi di sini.

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

DubDub
sumber
1

Menggunakan:

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

Ini memiliki manfaat berperilaku persis seperti Path.Combine.

TruthOf42
sumber
1

Inilah pendekatan saya dan saya akan menggunakannya untuk diri saya juga:

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);
}
Amit Bhagat
sumber
Ini hanya dapat diterima untuk kasus Anda. Ada beberapa kasus yang dapat merusak kode Anda. Juga, Anda tidak melakukan pengkodean yang benar dari bagian-bagian jalan. Ini bisa menjadi kerentanan besar ketika datang ke serangan scripting lintas situs.
Believe2014
Saya setuju dengan poin Anda. Kode seharusnya melakukan penggabungan sederhana dari dua bagian url.
Amit Bhagat
1

Gunakan ini:

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);
    }
}
Martin Murphy
sumber
Sentuhan yang bagus dengan 'WebPath'. :) Kode mungkin padat tapi tidak perlu - sulit bagi saya untuk melirik ini dan berkata, ya, itu sempurna. Itu membuat saya ingin melihat unit test. Mungkin itu hanya aku!
Brian MacKay
1
x.StartsWith ("/") &&! x.StartsWith ("http") - mengapa http memeriksa? apa yang kamu dapatkan?
penguat
Anda tidak ingin mencoba menghapus garis miring jika dimulai dengan http.
Martin Murphy
@BrianMacKay, saya tidak yakin dua liner menjamin tes unit tetapi jika Anda ingin merasa bebas untuk memberikan satu. Ini tidak seperti saya menerima tambalan atau apa pun, tetapi merasa bebas untuk mengedit saran.
Martin Murphy
1

Saya menemukan bahwa Urikonstruktor membalik '\' ke '/'. Jadi Anda juga bisa menggunakan Path.Combine, dengan Urikonstruktor.

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

Untuk apa nilainya, berikut beberapa metode ekstensi. Yang pertama akan menggabungkan jalur dan yang kedua menambahkan parameter ke 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();
    }
Lawman
sumber
1

Seperti yang ditemukan di jawaban lain, baik yang baru Uri()atau TryCreate()dapat melakukan centang. Namun, basis Uri harus diakhiri dengan /dan relatif harus TIDAK mulai dengan /; jika tidak, ia akan menghapus bagian akhir dari Url dasar

Saya pikir ini paling baik dilakukan sebagai metode ekstensi, yaitu

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

dan untuk menggunakannya:

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

Dalam hal kinerja, ini menghabiskan lebih banyak sumber daya daripada yang dibutuhkan, karena kelas Uri yang melakukan banyak parsing dan validasi; profil yang sangat kasar (Debug) melakukan sejuta operasi dalam waktu sekitar 2 detik. Ini akan berfungsi untuk sebagian besar skenario, namun agar lebih efisien, lebih baik memanipulasi segala sesuatu sebagai string, ini membutuhkan 125 milidetik untuk 1 juta operasi. Yaitu

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

Dan jika Anda masih ingin mengembalikan URI, dibutuhkan sekitar 600 milidetik untuk 1 juta operasi.

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

Saya harap ini membantu.

Mahmoud Hanafy
sumber
1

Saya pikir ini akan memberi Anda lebih banyak fleksibilitas karena Anda dapat menangani sebanyak mungkin segmen jalur yang Anda inginkan:

public static string UrlCombine(this string baseUrl, params string[] segments)
=> string.Join("/", new[] { baseUrl.TrimEnd('/') }.Concat(segments.Select(s => s.Trim('/'))));
Zaman keemasan
sumber