Mengapa HttpClient BaseAddress tidak berfungsi?

299

Pertimbangkan kode berikut, di mana BaseAddressmendefinisikan jalur URI parsial.

using (var handler = new HttpClientHandler())
using (var client = new HttpClient(handler))
{
    client.BaseAddress = new Uri("http://something.com/api");
    var response = await client.GetAsync("/resource/7");
}

Saya berharap ini melakukan GETpermintaan untuk http://something.com/api/resource/7. Tapi ternyata tidak.

Setelah beberapa pencarian, saya menemukan pertanyaan dan jawaban ini: HttpClient dengan BaseAddress . Sarannya adalah untuk menempatkan /pada akhir BaseAddress.

using (var handler = new HttpClientHandler())
using (var client = new HttpClient(handler))
{
    client.BaseAddress = new Uri("http://something.com/api/");
    var response = await client.GetAsync("/resource/7");
}

Itu masih tidak berhasil. Berikut dokumentasinya: HttpClient.BaseAddress Apa yang terjadi di sini?

Perisai Timothy
sumber
Kemungkinan duplikat HttpClient dengan BaseAddress
George Lanetz
@ ГеоргийЛанец Duplikat terbalik telah diajukan. Saya menulis pertanyaan ini secara khusus karena pertanyaan lain itu tidak ditulis dengan cara yang sangat mudah ditemukan oleh orang-orang dengan masalah yang sama, dan saya menulis jawabannya di sini karena jawaban di sana meninggalkan poin penting.
Timothy Shields
tetapi pertanyaan ini ditanyakan kemudian
George Lanetz
2
@ ГеоргийЛанец Itu bukan cara kerjanya. Biasanya pertanyaan yang paling "kanonik" adalah pertanyaan yang membuat duplikat menunjuk ke sana. Pertanyaan lain itu adalah tentang satu masalah yang dialami pengguna alih-alih membaca seperti FAQ.
Timothy Shields
2
@ ГеоргийЛанец Juga perhatikan saya referensi bahwa pertanyaan lain dalam pertanyaan ini, dan saya menjelaskan mengapa pertanyaan dan jawaban yang lain tidak cukup untuk menyelesaikan masalah.
Timothy Shields

Jawaban:

719

Ternyata, dari empat kemungkinan permutasi termasuk atau tidak termasuk trailing atau memimpin garis miring pada BaseAddressdan relatif URI beralih ke GetAsyncmetode - atau metode lain mana pun HttpClient- hanya satu permutasi yang berfungsi. Anda harus menempatkan garis miring di akhir BaseAddress, dan Anda tidak boleh menempatkan garis miring di awal URI relatif Anda, seperti dalam contoh berikut.

using (var handler = new HttpClientHandler())
using (var client = new HttpClient(handler))
{
    client.BaseAddress = new Uri("http://something.com/api/");
    var response = await client.GetAsync("resource/7");
}

Meskipun saya menjawab pertanyaan saya sendiri, saya pikir saya akan berkontribusi solusi karena, sekali lagi, perilaku tidak ramah ini tidak terdokumentasi. Rekan saya dan saya menghabiskan sebagian besar hari berusaha memperbaiki masalah yang akhirnya disebabkan oleh keanehan ini HttpClient.

Perisai Timothy
sumber
4
Terima kasih. Itu memecahkan masalah yang telah saya perjuangkan hampir selama dua hari sekarang, antara beralih ke Azure, kembali ke IIS, dan kembali ke IIS Express, yang paling kasar mengabaikan garis miring yang salah atau ekstra garis depan. Setelah diatur di kelas dasar saya RestClient, itu hampir tidak terlihat dan tidak mendapat perhatian sama sekali, dan saya tidak pernah melihat url penuh di breakpoints saya dll.
ProfK
43
Saya dapat mengkonfirmasi bahwa keanehan ini (dan perbaikan ini) masih relevan di .NET Core. Terima kasih telah mengurangi Timothy saya yang menarik rambut.
Nate Barbettini
8
Ini karena tanpa membuntuti garis miring ketika membuat permintaan, ia akan menghapus bagian terakhir. Jadi hits sesuatu.com/resource/7 . Jika Anda menetapkan alamat dasar sebagai sesuatu / com (tidak masalah jika dengan atau tanpa trailing slash) juga tidak masalah jika Anda meletakkan slash di awal api / resource / 7. Tanpa trailing slash, bagian terakhir dari alamat dasar diperlakukan seperti file dan dijatuhkan ketika mem-bulding permintaan.
Piotr Perak
12
Ini tidak secara langsung menangani pertanyaan awal tetapi terkait. Per Mircosoft, turunan dari HttpClient () harus ditugaskan ke variabel statis dan digunakan kembali ( docs.microsoft.com/en-us/aspnet/web-api/overview/advanced/… - Creating a new HttpClient instance per request can exhaust the available sockets). Jadi Anda harus mempertimbangkan untuk menghapus Using ().
sanmcp
6
Hanya implementasi yang mengerikan. Mengapa mereka tidak memperbaikinya?
timmkrause
55

Resolusi Referensi dijelaskan oleh RFC 3986 Uniform Resource Identifier (URI): Generic Syntax . Dan itulah tepatnya cara kerjanya. Untuk mempertahankan jalur URI dasar, Anda perlu menambahkan garis miring di akhir URI dasar dan menghapus garis miring pada awal URI relatif.

Jika basis URI berisi jalur yang tidak kosong, gabungkan prosedur membuang itu bagian terakhir (setelah terakhir /). Bagian yang relevan :

5.2.3. Gabungkan Jalur

Pseudocode di atas mengacu pada rutin "gabungan" untuk menggabungkan referensi jalur relatif dengan jalur basis URI. Ini dicapai sebagai berikut:

  • Jika pangkalan URI memiliki komponen otoritas yang ditentukan dan jalur kosong, lalu kembalikan string yang terdiri dari "/" yang disatukan dengan jalur referensi; jika tidak

  • mengembalikan string yang terdiri dari komponen jalur referensi ditambahkan ke semua kecuali segmen terakhir dari jalur URI basis (yaitu, mengecualikan karakter apa pun setelah "/" paling kanan di jalur URI dasar, atau mengecualikan seluruh jalur URI basis jika tidak mengandung karakter "/" apa pun).

Jika URI relatif dimulai dengan garis miring, itu disebut URI relatif jalur absolut. Dalam hal ini prosedur gabungan mengabaikan semua jalur URI dasar. Untuk informasi lebih lanjut, periksa 5.2.2. Ubah bagian Referensi .

Leonid Vasilev
sumber
3
Pustaka klien yang bagus tetapi seperti HttpClient seharusnya melindungi kami dari detail implementasi esoteris seperti ini.
Jamie Ide
-1

Terjadi masalah dengan HTTPClient, bahkan dengan saran masih belum dapat mengautentikasi. Ternyata saya membutuhkan trailing '/' di jalur relatif saya.

yaitu

var result = await _client.GetStringAsync(_awxUrl + "api/v2/inventories/?name=" + inventoryName);
var result = await _client.PostAsJsonAsync(_awxUrl + "api/v2/job_templates/" + templateId+"/launch/" , new {
                inventory = inventoryId
            });
Tony
sumber
-6

Atau - jangan gunakan BaseAddresssama sekali. Masukkan seluruh URL ke dalam GetAsync()

userSteve
sumber
33
Tidak menjawab pertanyaan apa pun.
Archibald
7
BaseAddress mengurangi kebisingan. Di mata saya. :)
MetalMikester
2
Saya harus tidak setuju dengan komentar negatif. Saya telah menghabiskan 2 hari untuk mencari tahu mengapa panggilan HttpClient saya bekerja pada PC Dev saya tetapi istirahat di server. Anehnya Powershell berfungsi tetapi .net tidak. Saya menggunakan .SendAsyc. Kemudian saya menemukan bahwa .GetAsyc bekerja. Ini menuntun saya ke jalan yang berbeda dan akhirnya di sini. Menambahkan atau menghapus / antara Alamat Base dan URL Relatif tidak melakukan apa-apa. Masih mendapat 404 kesalahan .... namun ketika saya tidak mengatur Alamat Pangkalan, dan menempatkan seluruh path ke dalam Relatif .. itu berhasil! Sekali lagi, ini dengan .SendAsync tetapi ada 2 hari saya tidak akan pernah kembali!
da_jokker