Невозможно установить некоторые HTTP-заголовки при использовании System.Net.WebRequest

Когда я пытаюсь добавить пару ключа/значения HTTP-заголовка в объекте WebRequest, я получаю следующее исключение:

Этот заголовок должен быть изменен с использованием соответствующего свойства

Я попытался добавить новые значения в коллекцию Headers с помощью метода Add(), но я все равно получаю то же исключение.

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

Я могу обойти это, отбросив объект WebRequest до HttpWebRequest и установив такие свойства, как httpWebReq.Referer ="http://stackoverflow.com", но это работает только для нескольких заголовков, которые отображаются через свойства.

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

Ответ 1

Если вам нужен короткий и технический ответ, перейдите к последнему разделу ответа.

Если вы хотите узнать лучше, прочитайте все, и я надеюсь, вам понравится...


Я сегодня тоже справился с этой проблемой, и сегодня я обнаружил, что:

  • приведенные выше ответы верны:

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

    1.2 Каждый раз, когда вы меняете заголовки HttpWebRequest, вам нужно использовать соответствующие свойства для самого объекта, если они существуют.

Спасибо FOR и 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)) перед вызовом add

Ответ 2

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

Ex 1

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

Ex 2

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

Ответ 3

Все предыдущие ответы описывают проблему без предоставления решения. Вот метод расширения, который решает проблему, позволяя вам устанавливать любой заголовок через его имя строки.

Использование

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, в котором вам нужно взять заголовки в запросе и перенаправить их получателю.

Есть много других сценариев, где его просто не практично или невозможно использовать свойства. Принуждение пользователя к настройке заголовка через свойство является очень негибким дизайном, поэтому требуется отражение. Верхняя сторона заключается в том, что отражение абстрагируется, оно все еще быстрое (.001 секунда в моих тестах) и как метод расширения чувствует себя естественным.

Примечания

Имена заголовков нечувствительны к регистру для RFC, http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2

Ответ 4

В любое время, когда вы меняете заголовки HttpWebRequest, вам нужно использовать соответствующие свойства для самого объекта, если они существуют. Если у вас есть простой WebRequest, обязательно сначала поставьте его на HttpWebRequest. Затем Referrer в вашем случае можно получить доступ через ((HttpWebRequest)request).Referrer, поэтому вам не нужно напрямую изменять заголовок - просто установите свойство в нужное значение. ContentLength, ContentType, UserAgent и т.д., все должны быть установлены таким образом.

IMHO, это недостаток в части MS... установка заголовков через Headers.Add() должна автоматически вызывать соответствующее свойство за кулисами, если это то, что они хотят делать.

Ответ 5

У меня было то же исключение, когда мой код попытался установить значение заголовка "Accept" следующим образом:

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

Ответ 6

WebRequest является абстрактным (и поскольку любой наследующий класс должен переопределить свойство Headers), какой конкретный WebRequest вы используете? Другими словами, как вы можете получить объект WebRequest для beign с?

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

Другие классы, наследующие WebRequest, могут иметь еще лучшие свойства, обертывающие определенные заголовки; См. этот пост, например.

Ответ 7

В принципе, нет. Это заголовок http, поэтому разумно отбрасывать HttpWebRequest и устанавливать .Referer (как вы указываете в вопросе):

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

Ответ 8

Вышеуказанные ответы все в порядке, но суть проблемы в том, что некоторые заголовки заданы одним способом, а другие - другими способами. См. Выше для списков с ограниченным заголовком. В этом вы просто установили их как свойство. Для других вы фактически добавляете заголовок. См. Здесь.

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

    request.Accept = "application/json";

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

Ответ 9

Я использую только:

request.ContentType = "application/json; charset=utf-8"

Ответ 10

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

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

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

request.Referer = "yourReferer";

Эти свойства доступны в объекте запроса.