Не удается установить некоторые заголовки HTTP при использовании System.Net.WebRequest - PullRequest
124 голосов
/ 27 октября 2008

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

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

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

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

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

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

Ответы [ 10 ]

177 голосов
/ 20 января 2011

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

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


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

  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 класс.


Редактировать: (полезно, из комментариев, комментариев пользователя Кайдо )

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

71 голосов
/ 09 марта 2010

Я столкнулся с этой проблемой с пользовательским веб-клиентом. Я думаю, что люди могут запутаться из-за нескольких способов сделать это. При использовании WebRequest.Create() вы можете привести к HttpWebRequest и использовать свойство для добавления или изменения заголовка. При использовании 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();
29 голосов
/ 15 апреля 2014

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

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

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

12 голосов
/ 01 декабря 2008

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

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

11 голосов
/ 16 марта 2016

У меня было то же исключение, когда мой код пытался установить значение заголовка "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";
7 голосов
/ 27 октября 2008

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

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

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

2 голосов
/ 30 декабря 2014

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

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

    request.Accept = "application/json";

    request.Headers.Add(HttpRequestHeader.Authorization, "Basic " + info.clientId + ":" + info.clientSecret);
1 голос
/ 27 октября 2008

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

HttpWebRequest req = ...
req.Referer = "your url";
0 голосов
/ 24 апреля 2015

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

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

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

request.Referer = "yourReferer";

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

0 голосов
/ 11 марта 2015

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

request.ContentType = "application/json; charset=utf-8"
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...