Неможливо встановити деякі заголовки HTTP при використанні System.Net.WebRequest


130

Коли я намагаюся додати пару WebRequestоб’єкта ключ / значення HTTP на об’єкт, я отримую таке виняток:

Це заголовок необхідно змінити, використовуючи відповідне властивість

Я намагався додати нові значення до Headersколекції за допомогою методу Add (), але все одно отримую те саме виняток.

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

Я можу обійти це питання, передавши об’єкт WebRequest на HttpWebRequest і встановивши такі властивості, як httpWebReq.Referer ="http://stackoverflow.com", але це працює лише для декількох заголовків, які піддаються впливу властивостей.

Мені хотілося б знати, чи є спосіб отримати більш тонкий контроль над зміною заголовків із запитом на віддалений ресурс.

Відповіді:


182

Якщо вам потрібна коротка та технічна відповідь, перейдіть направо до останнього розділу відповіді.

Якщо ви хочете знати краще, прочитайте все, і я сподіваюся, що вам сподобається ...


Я вирішив цю проблему і сьогодні, і що я виявив сьогодні, це:

  1. наведені вище відповіді вірні, як:

    1.1 це говорить вам про те, що заголовок, який ви намагаєтеся додати, вже існує, і вам слід змінити його значення за допомогою відповідної властивості (наприклад, індексатора), а не намагатися додати його ще раз.

    1.2 Щоразу, коли ви змінюєте заголовки HttpWebRequest, вам потрібно використовувати відповідні властивості самого об’єкта, якщо вони існують.

Дякуємо ЗА та Jvenema за провідні рекомендації ...

  1. Але те, що я з'ясував, і це був пропущений фрагмент головоломки, це те, що:

    2.1 WebHeaderCollectionКлас зазвичай доступний через WebRequest.Headers або WebResponse.Headers. Деякі загальні заголовки вважаються обмеженими і піддаються впливу безпосередньо API (типу Content-Type) або захищені системою, і їх неможливо змінити.

Обмежені заголовки:

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

Отже, наступного разу, коли ви зіткнетеся з цим винятком і не знаєте, як це вирішити, пам’ятайте, що існують деякі обмежені заголовки, і рішення - змінити їх значення, використовуючи відповідне властивість явно з WebRequest/ HttpWebRequestкласу.


Редагувати: (корисно, з коментарів, коментарів користувача Kaido )

Рішення полягає в тому, щоб перевірити, чи заголовок вже існує чи обмежений ( WebHeaderCollection.IsRestricted(key)) перед викликом додати


8
"змінити свої значення, використовуючи відповідну властивість", йдеться про все
CRice

76
Ця відповідь просто повторює Повідомлення про винятки, не даючи рішення проблеми.
000

11
Рішення полягає в тому, щоб перевірити, чи заголовок вже існує чи обмежений (WebHeaderCollection.IsRestricted (ключ)) перед тим, як викликати add
Kaido

7
@Sam прочитати розділ 1.1, який вирішує проблему. це означає, що властивість, яку ми намагаємося додати через, Headers.Add()вже існує, тому слід замінити її.
Джунайд Кадір

4
"Я вважаю, що важливо зазначити, що це обмеження є особливістю .NET Framework" - я б швидше не мав такої функції.
Герберт Амарал

76

Я зіткнувся з цією проблемою із користувацьким веб-клієнтом. Я думаю, що люди можуть заплутатися через кілька способів зробити це. Під час використання WebRequest.Create()ви можете передати на HttpWebRequestта використовувати властивість для додавання чи зміни заголовка. Під час використання a WebHeaderCollectionви можете використовувати .Add("referer","my_url").

Вихід 1

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

Вихід 2

HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
request.Referer = "http://stackoverflow.com";
request.UserAgent = "Mozilla/5.0";
response = (HttpWebResponse)request.GetResponse();

1
Ex 1 вирішив мою проблему за допомогою цього винятку. Тому я змінив client.Headers ["referer"] = url; to client.Headers.Add ("референт", URL); і справи працюють. Дякую.
000

2
майте на увазі, що ця відповідь містить щасливе припущення, що ви працюєте на робочому столі .Net під час виконання програми та просите http. WebRequest.Create може повертати безліч різних об'єктів залежно від того, який префікс протоколу ви використовуєте. Це пов'язано з CustomProtocolHandlers, якщо хтось цікавиться ними. І на WP7 або Silverlight класи впровадження запиту теж трохи відрізняються. Просто будьте обережні з цим.
quetzalcoatl

1
Але я не можу змінити заголовок "Прийняти". Як я можу це змінити?
користувач

Перший приклад ще дає мені ту ж помилку
mrid

29

Усі попередні відповіді описують проблему, не надаючи рішення. Ось метод розширення, який вирішує проблему, дозволяючи встановити будь-який заголовок за допомогою його рядкового імені.

Використання

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

Розширення класу

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

Сценарії

Я написав обгортку HttpWebRequestі не хотів виставляти всі 13 заголовків з обмеженими можливостями як властивості в моїй обгортці. Натомість я хотів використовувати простий Dictionary<string, string>.

Інший приклад - проксі-сервер HTTP, де потрібно взяти заголовки у запиті та переслати їх одержувачу.

Існує маса інших сценаріїв, де просто не практично або можливо використовувати властивості. Змусити користувача встановити заголовок через властивість - це дуже негнучка конструкція, тому потрібне роздуми. Спередом є те, що відбиття відводиться далеко, воно все ще швидко (0,001 секунди в моїх тестах), і як метод розширення відчуває себе природним.

Примітки

Назви заголовків не залежать від регістру відповідно до RFC, http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2


я використовую його для проксі-з'єднання, але після того, як він скаже, так, я містять ключ для "проксі-з'єднання", він повертає нульове значення, що призводить до нульового посилання виключення
deadManN

Дякую за розумне виправлення. Я дозволяю розширення встановити всі заголовки: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

У мене був такий самий виняток, коли мій код намагався встановити значення заголовка "Прийняти" таким чином:

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

Рішення полягало в тому, щоб змінити це на це:

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

12

Щоразу, коли ви змінюєте заголовки HttpWebRequest, вам потрібно використовувати відповідні властивості самого об’єкта, якщо вони існують. Якщо у вас є звичайна WebRequest, не забудьте передати її HttpWebRequestпершою. Тоді Referrerу вашому випадку можна отримати доступ через ((HttpWebRequest)request).Referrer, так що вам не потрібно безпосередньо змінювати заголовок - просто встановіть властивість на потрібне значення. ContentLength, ContentType, UserAgentІ т.д., все повинні бути встановлені таким чином.

IMHO, це недолік у частині MS ... встановлення заголовків через Headers.Add()автоматично повинно викликати відповідну властивість за кадром, якщо це те, що вони хочуть зробити.


7

WebRequest є абстрактним (і оскільки будь-який клас успадкування повинен перекривати властивість заголовків) .. який конкретний WebRequest ви використовуєте? Іншими словами, як змусити цей об’єкт WebRequest призначати?

ehr .. mnour відповідь дала мені зрозуміти, що повідомлення про помилку, яке ви отримували, насправді є місцем: воно говорить вам про те, що заголовок, який ви намагаєтеся додати, вже існує, і вам слід змінити його значення, використовуючи відповідну властивість (наприклад, індексатор ), а не намагатися додати його ще раз. Це, мабуть, все, що ви шукали.

Інші класи, що успадковують WebRequest, можуть мати ще кращі властивості, що обгортають певні заголовки; Наприклад, дивіться цю публікацію .


Насправді WebRequest.Create (URL) створює примірник об’єкта WebRequest.
Ігал Табачник

2

Наведені вище відповіді все добре, але суть питання полягає в тому, що одні заголовки встановлені одним способом, а інші - іншими способами. Дивіться вище для списків із обмеженим заголовком. Для цього ви просто встановите їх як власність. Для інших ви фактично додаєте заголовок. Дивіться тут.

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

    request.Accept = "application/json";

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

1

В основному, ні. Це заголовок http, тому доцільно подати HttpWebRequestта встановити .Referer(як ви вказали у запитанні):

HttpWebRequest req = ...
req.Referer = "your url";

1

Примітка: це рішення буде працювати з WebClientSocket, а також з HttpWebRequest або будь-яким іншим класом, який використовує WebHeaderCollection для роботи з заголовками.

Якщо ви подивитеся на вихідний код WebHeaderCollection.cs, ви побачите, що Hinfo використовується для зберігання інформації всіх відомих заголовків:

private static readonly HeaderInfoTable HInfo = new HeaderInfoTable();

Переглядаючи клас HeaderInfoTable, ви можете помітити, що всі дані зберігаються в хеш-таблиці

private static Hashtable HeaderHashTable;

Крім того, у статичному контролері HeaderInfoTable ви можете побачити, що всі відомі заголовки додаються в масив HeaderInfo і потім скопіюються в хештеб.

Заключний вигляд класу HeaderInfo показує назви полів.

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

Отже, з усього вищесказаного, ось код, який використовує відображення для пошуку статичного Hashtable у класі HeaderInfoTable, а потім змінює кожний HeaderInfo з обмеженим запитом всередині хеш-таблиці, щоб бути необмеженим.

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

                        }
                    }

                }
            }
        } 

Блискуче! Це також дозволяє встановити ті заголовки для запиту, який використовується під час налаштування веб-сокетів і тим самим обробляти
Øystein Kolsrud

Це має бути так, оскільки всі вони використовують WebHeaderCollection для маніпулювання заголовками. Я перевірив це тільки на HttpWebRequest.
Сплячий


0

Ви можете просто передати WebRequest на HttpWebRequest, показаний нижче:

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

а потім замість того, щоб намагатися маніпулювати списком заголовків, застосуйте його безпосередньо у запиті властивості запиту.Referer:

request.Referer = "yourReferer";

Ці властивості доступні в об’єкті запиту.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.