Tidak dapat mengatur beberapa tajuk HTTP saat menggunakan System.Net.WebRequest

130

Ketika saya mencoba menambahkan pasangan kunci HTTP / nilai pada WebRequestobjek, saya mendapatkan pengecualian berikut:

Header ini harus dimodifikasi menggunakan properti yang sesuai

Saya sudah mencoba menambahkan nilai baru ke Headerskoleksi dengan menggunakan metode Add () tapi saya masih mendapatkan pengecualian yang sama.

webRequest.Headers.Add(HttpRequestHeader.Referer, "http://stackoverflow.com");

Saya bisa menyiasati ini dengan melemparkan objek WebRequest ke HttpWebRequest dan mengatur properti seperti httpWebReq.Referer ="http://stackoverflow.com", tetapi ini hanya berfungsi untuk beberapa header yang diekspos melalui properti.

Saya ingin tahu apakah ada cara untuk mendapatkan kontrol yang lebih baik atas memodifikasi header dengan permintaan untuk sumber daya jarak jauh.

Pisau cukur
sumber

Jawaban:

182

Jika Anda membutuhkan jawaban singkat dan teknis, langsung ke bagian terakhir dari jawabannya.

Jika Anda ingin tahu lebih baik, baca semuanya, dan saya harap Anda akan menikmati ...


Saya membalas masalah ini juga hari ini, dan yang saya temukan hari ini adalah:

  1. jawaban di atas benar, seperti:

    1.1 ia memberi tahu Anda bahwa tajuk yang Anda coba tambahkan sudah ada dan Anda kemudian harus memodifikasi nilainya menggunakan properti yang sesuai (pengindeks, misalnya), alih-alih mencoba menambahkannya lagi.

    1.2 Kapan pun Anda mengubah tajuk HttpWebRequest, Anda harus menggunakan properti yang sesuai pada objek itu sendiri, jika ada.

Terima kasih untuk dan Jvenema untuk pedoman terkemuka ...

  1. Tapi, apa yang saya temukan, dan itu adalah bagian yang hilang dalam teka-teki itu adalah:

    2.1 WebHeaderCollectionKelas umumnya diakses melalui. WebRequestPemimpin atau WebResponse. Pemimpin. Beberapa tajuk umum dianggap dibatasi dan terpapar langsung oleh API (seperti Jenis Konten) atau dilindungi oleh sistem dan tidak dapat diubah.

Header yang dibatasi adalah:

  • Accept
  • Connection
  • Content-Length
  • Content-Type
  • Date
  • Expect
  • Host
  • If-Modified-Since
  • Range
  • Referer
  • Transfer-Encoding
  • User-Agent
  • Proxy-Connection

Jadi, lain kali Anda menghadapi pengecualian ini dan tidak tahu bagaimana menyelesaikannya, ingatlah bahwa ada beberapa header terbatas, dan solusinya adalah mengubah nilai-nilai mereka menggunakan properti yang sesuai secara eksplisit dari kelas WebRequest/ HttpWebRequest.


Edit: (berguna, dari komentar, komentar oleh pengguna Kaido )

Solusi adalah untuk memeriksa apakah header sudah ada atau dibatasi ( WebHeaderCollection.IsRestricted(key)) sebelum memanggil add

dubi
sumber
8
"modifikasi nilai-nilai mereka menggunakan properti yang sesuai" kata itu semua
CRice
76
Jawaban ini hanya mengulangi pesan pengecualian tanpa memberikan solusi untuk masalah tersebut.
000
11
Solusi adalah memeriksa apakah tajuk sudah ada atau dibatasi (WebHeaderCollection.IsR dibatasi (kunci)) sebelum memanggil add
Kaido
7
@ Sam membaca bagian 1.1 yang memecahkan masalah. itu berarti properti yang kami coba tambahkan melalui Headers.Add()sudah ada karena itu kita harus memodifikasinya.
Junaid Qadir
4
"Saya merasa penting untuk menunjukkan bahwa pembatasan ini adalah fitur dari .NET Framework" - Saya lebih suka tidak memiliki fitur semacam ini.
Herberth Amaral
76

Saya mengalami masalah ini dengan klien web kustom. Saya pikir orang mungkin menjadi bingung karena beberapa cara untuk melakukan ini. Saat menggunakan, WebRequest.Create()Anda dapat melakukan cast ke HttpWebRequestdan menggunakan properti untuk menambah atau memodifikasi header. Saat menggunakan, WebHeaderCollectionAnda dapat menggunakan .Add("referer","my_url").

Kel 1

WebClient client = new WebClient();
client.Headers.Add("referer", "http://stackoverflow.com");
client.Headers.Add("user-agent", "Mozilla/5.0");

Kel 2

HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
request.Referer = "http://stackoverflow.com";
request.UserAgent = "Mozilla/5.0";
response = (HttpWebResponse)request.GetResponse();
Chmod
sumber
1
Ex 1 memecahkan masalah saya dengan pengecualian ini. Jadi saya mengubah client.Headers ["referer"] = url; ke client.Headers.Add ("referer", url); dan semuanya mulai bekerja. Terima kasih.
000
2
berhati-hatilah bahwa jawaban ini mengandung asumsi bahagia bahwa Anda sedang mengerjakan desktop .Net runtime dan meminta http. WebRequest.Create dapat mengembalikan berbagai objek berbeda tergantung pada awalan protokol apa yang Anda gunakan. Ini terkait dengan CustomProtocolHandlers jika ada yang tertarik pada mereka .. Dan pada WP7 atau Silverlight, kelas implementasi permintaan juga sedikit berbeda. Berhati-hatilah dengan ini.
quetzalcoatl
1
Tapi saya tidak bisa memodifikasi header "Terima". Bagaimana saya bisa memodifikasi ini?
pengguna
Contoh pertama masih memberi saya kesalahan yang sama
mrid
29

Semua jawaban sebelumnya menggambarkan masalah tanpa memberikan solusi. Berikut adalah metode ekstensi yang memecahkan masalah dengan memungkinkan Anda untuk mengatur header apa pun melalui nama string-nya.

Pemakaian

HttpWebRequest request = WebRequest.Create(url) as HttpWebRequest;
request.SetRawHeader("content-type", "application/json");

Kelas Ekstensi

public static class HttpWebRequestExtensions
{
    static string[] RestrictedHeaders = new string[] {
            "Accept",
            "Connection",
            "Content-Length",
            "Content-Type",
            "Date",
            "Expect",
            "Host",
            "If-Modified-Since",
            "Keep-Alive",
            "Proxy-Connection",
            "Range",
            "Referer",
            "Transfer-Encoding",
            "User-Agent"
    };

    static Dictionary<string, PropertyInfo> HeaderProperties = new Dictionary<string, PropertyInfo>(StringComparer.OrdinalIgnoreCase);

    static HttpWebRequestExtensions()
    {
        Type type = typeof(HttpWebRequest);
        foreach (string header in RestrictedHeaders)
        {
            string propertyName = header.Replace("-", "");
            PropertyInfo headerProperty = type.GetProperty(propertyName);
            HeaderProperties[header] = headerProperty;
        }
    }

    public static void SetRawHeader(this HttpWebRequest request, string name, string value)
    {
        if (HeaderProperties.ContainsKey(name))
        {
            PropertyInfo property = HeaderProperties[name];
            if (property.PropertyType == typeof(DateTime))
                property.SetValue(request, DateTime.Parse(value), null);
            else if (property.PropertyType == typeof(bool))
                property.SetValue(request, Boolean.Parse(value), null);
            else if (property.PropertyType == typeof(long))
                property.SetValue(request, Int64.Parse(value), null);
            else
                property.SetValue(request, value, null);
        }
        else
        {
            request.Headers[name] = value;
        }
    }
}

Skenario

Saya menulis pembungkus untuk HttpWebRequestdan tidak ingin mengekspos ke-13 header terbatas sebagai properti di bungkus saya. Sebaliknya saya ingin menggunakan yang sederhana Dictionary<string, string>.

Contoh lain adalah proksi HTTP di mana Anda perlu mengambil tajuk dalam permintaan dan meneruskannya ke penerima.

Ada banyak skenario lain di mana tidak praktis atau mungkin untuk menggunakan properti. Memaksa pengguna untuk mengatur header melalui properti adalah desain yang sangat tidak fleksibel, oleh karena itu diperlukan refleksi. Sisi baiknya adalah refleksi itu disarikan, masih cepat (0,001 detik dalam tes saya), dan sebagai metode ekstensi terasa alami.

Catatan

Nama tajuk tidak sensitif huruf per RFC, http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2

Despertar
sumber
saya menggunakannya untuk Proxy-Connection, tetapi setelah itu mengatakan, ya saya mengandung kunci untuk "Proxy-Connection" itu mengembalikan nol, yang mengarah pada pengecualian referensi nol
deadManN
Terima kasih atas perbaikan cerdasnya. Saya membiarkan ekstensi mengatur semua header:static Dictionary<string, PropertyInfo> HeaderProperties = new Dictionary<string, PropertyInfo>(StringComparer.InvariantCultureIgnoreCase); static WebRequestExtensions() { // Get property info for restricted headers. Type type = typeof(HttpWebRequest); foreach (string header in Enum.GetNames(typeof(HttpRequestHeader))) { var property = type.GetProperty(header.ToString()); if (property != null) { HeaderProperties.Add(property.Name, property); } } }
Suncat2000
13

Saya memiliki pengecualian yang sama ketika kode saya mencoba mengatur nilai tajuk "Terima" seperti ini:

WebRequest request = WebRequest.Create("http://someServer:6405/biprws/logon/long");
request.Headers.Add("Accept", "application/json");

Solusinya adalah mengubahnya menjadi ini:

HttpWebRequest request = (HttpWebRequest)WebRequest.Create("http://someServer:6405/biprws/logon/long");
request.Accept = "application/json";
Mike Gledhill
sumber
12

Setiap kali Anda mengubah tajuk HttpWebRequest, Anda harus menggunakan properti yang sesuai pada objek itu sendiri, jika ada. Jika Anda memiliki dataran WebRequest, pastikan untuk melemparkannya HttpWebRequestterlebih dahulu. Kemudian Referrerdalam kasus Anda dapat diakses melalui ((HttpWebRequest)request).Referrer, sehingga Anda tidak perlu memodifikasi header secara langsung - cukup atur properti ke nilai yang tepat. ContentLength, ContentType, UserAgent, Dll, semua perlu diatur dengan cara ini.

IMHO, ini adalah kelemahan pada bagian MS ... mengatur header melalui Headers.Add()harus secara otomatis memanggil properti yang sesuai di belakang layar, jika itu yang ingin mereka lakukan.

jvenema
sumber
7

WebRequest bersifat abstrak (dan karena setiap kelas warisan harus menimpa properti Header) .. WebRequest konkret apa yang Anda gunakan? Dengan kata lain, bagaimana Anda mendapatkan objek WebRequest yang akan ditandatangani?

ehr .. jawaban saya membuat saya menyadari bahwa pesan kesalahan yang Anda peroleh sebenarnya tepat: ia memberi tahu Anda bahwa tajuk yang Anda coba tambahkan sudah ada dan kemudian Anda harus mengubah nilainya menggunakan properti yang sesuai (pengindeks, misalnya, ), alih-alih mencoba menambahkannya lagi. Mungkin itu yang Anda cari.

Kelas lain yang mewarisi dari WebRequest mungkin memiliki properti yang lebih baik yang membungkus header tertentu; Lihat posting ini misalnya.

UNTUK
sumber
Sebenarnya WebRequest.Create (url) membuat instance objek WebRequest.
Igal Tabachnik
2

Jawaban di atas semuanya baik-baik saja, tetapi inti dari masalah ini adalah bahwa beberapa tajuk diatur satu arah, dan yang lain diatur dengan cara lain. Lihat di atas untuk daftar 'header terbatas'. Untuk ini, Anda cukup menetapkannya sebagai properti. Untuk yang lain, Anda sebenarnya menambahkan tajuk. Lihat disini.

    request.ContentType = "application/x-www-form-urlencoded";

    request.Accept = "application/json";

    request.Headers.Add(HttpRequestHeader.Authorization, "Basic " + info.clientId + ":" + info.clientSecret);
rampok
sumber
1

Pada dasarnya tidak. Itu adalah tajuk http, jadi masuk akal untuk menggunakan HttpWebRequestdan menyetel .Referer(seperti yang Anda tunjukkan dalam pertanyaan):

HttpWebRequest req = ...
req.Referer = "your url";
Marc Gravell
sumber
1

Catatan: solusi ini akan bekerja dengan WebClientSocket serta dengan HttpWebRequest atau kelas lain yang menggunakan WebHeaderCollection untuk bekerja dengan header.

Jika Anda melihat kode sumber WebHeaderCollection.cs Anda akan melihat bahwa Hinfo digunakan untuk menyimpan informasi dari semua header yang dikenal:

private static readonly HeaderInfoTable HInfo = new HeaderInfoTable();

Melihat kelas HeaderInfoTable, Anda dapat melihat semua data disimpan ke dalam tabel hash

private static Hashtable HeaderHashTable;

Selanjutnya, dalam kontruktor statis HeaderInfoTable, Anda dapat melihat semua header yang dikenal ditambahkan dalam array HeaderInfo dan kemudian disalin ke dalam hashtable.

Tampilan akhir di kelas HeaderInfo menunjukkan nama bidang.

internal class HeaderInfo {

    internal readonly bool IsRequestRestricted;
    internal readonly bool IsResponseRestricted;
    internal readonly HeaderParser Parser;

    //
    // Note that the HeaderName field is not always valid, and should not
    // be used after initialization. In particular, the HeaderInfo returned
    // for an unknown header will not have the correct header name.
    //

    internal readonly string HeaderName;
    internal readonly bool AllowMultiValues;
    ...
    }

Jadi, dengan semua hal di atas, berikut adalah kode yang menggunakan refleksi untuk menemukan Hashtable statis di kelas HeaderInfoTable dan kemudian mengubah setiap HeaderInfo permintaan-dibatasi di dalam tabel hash menjadi tidak dibatasi

        // use reflection to remove IsRequestRestricted from headerInfo hash table
        Assembly a = typeof(HttpWebRequest).Assembly;
        foreach (FieldInfo f in a.GetType("System.Net.HeaderInfoTable").GetFields(BindingFlags.NonPublic | BindingFlags.Static))
        {
            if (f.Name == "HeaderHashTable")
            {
                Hashtable hashTable = f.GetValue(null) as Hashtable;
                foreach (string sKey in hashTable.Keys)
                {

                    object headerInfo = hashTable[sKey];
                    //Console.WriteLine(String.Format("{0}: {1}", sKey, hashTable[sKey]));
                    foreach (FieldInfo g in a.GetType("System.Net.HeaderInfo").GetFields(BindingFlags.NonPublic | BindingFlags.Instance))
                    {

                        if (g.Name == "IsRequestRestricted")
                        {
                            bool b = (bool)g.GetValue(headerInfo);
                            if (b)
                            {
                                g.SetValue(headerInfo, false);
                                Console.WriteLine(sKey + "." + g.Name + " changed to false");
                            }

                        }
                    }

                }
            }
        } 
Tidur
sumber
Cemerlang! Ini juga memungkinkan untuk mengatur tajuk tersebut untuk permintaan yang digunakan saat mengatur soket web dan dengan demikian mengatasi masalah ini: github.com/dotnet/corefx/issues/26627
Øystein Kolsrud
Itu harus terjadi karena mereka semua menggunakan WebHeaderCollection untuk memanipulasi header. Saya telah mengujinya hanya di HttpWebRequest.
Tidur
0

Saya hanya menggunakan:

request.ContentType = "application/json; charset=utf-8"
Stefan Michev
sumber
0

Anda bisa mengirimkan WebRequest ke HttpWebRequest yang ditunjukkan di bawah ini:

var request = (HttpWebRequest)WebRequest.Create(myUri);

dan kemudian alih-alih mencoba memanipulasi daftar tajuk, menerapkannya langsung di permintaan properti permintaan.

request.Referer = "yourReferer";

Properti ini tersedia di objek permintaan.

Bonomi
sumber