web-dev-qa-db-fra.com

Impossible de définir certains en-têtes HTTP lors de l'utilisation de System.Net.WebRequest

Lorsque j'essaie d'ajouter une paire clé/valeur d'en-tête HTTP sur un objet WebRequest, j'obtiens l'exception suivante:

Cet en-tête doit être modifié à l'aide de la propriété appropriée

J'ai essayé d'ajouter de nouvelles valeurs à la collection Headers à l'aide de la méthode Add (), mais j'obtiens toujours la même exception.

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

Je peux contourner ce problème en convertissant l'objet WebRequest en un HttpWebRequest et en définissant les propriétés telles que httpWebReq.Referer ="http://stackoverflow.com", mais cela ne fonctionne que pour une poignée d'en-têtes exposés via des propriétés.

J'aimerais savoir s'il existe un moyen d'obtenir un contrôle plus fin de la modification des en-têtes avec une demande de ressource distante.

124
Vince Panuccio

Si vous avez besoin d'une réponse brève et technique, allez directement à la dernière section de la réponse.

Si vous voulez en savoir plus, lisez tout et j'espère que vous apprécierez ...


J'ai également contré ce problème aujourd'hui, et ce que j'ai découvert aujourd'hui, c'est que:

  1. les réponses ci-dessus sont vraies, comme:

    1.1, il vous dit que l'en-tête que vous essayez d'ajouter existe déjà et vous devriez alors modifier sa valeur en utilisant la propriété appropriée (l'indexeur, par exemple), au lieu d'essayer de l'ajouter à nouveau.

    1.2 Chaque fois que vous modifiez les en-têtes d'un HttpWebRequest, vous devez utiliser les propriétés appropriées sur l'objet lui-même, si elles existent.

Merci pour et Jvenema pour les directives de premier plan ...

  1. Mais, ce que j’ai découvert et , c’était la pièce manquante du puzzle , c’est que:

    2.1 La classe WebHeaderCollection est généralement accessible via WebRequest. En-têtes ou WebResponse. En-têtes. Certains en-têtes courants sont considérés comme restreints et sont soit exposés directement par l'API (tels que Content-Type), soit protégés par le système et ne peuvent pas être modifiés.

Les en-têtes restreints sont:

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

Ainsi, la prochaine fois que vous rencontrerez cette exception et que vous ne savez pas comment le résoudre, souvenez-vous qu'il existe des en-têtes restreints et que la solution consiste à modifier leurs valeurs à l'aide de la propriété appropriée de manière explicite à partir de WebRequest/HttpWebRequest classe.


Edit: (utile, à partir de commentaires, commentaire par utilisateur Kaido )

La solution consiste à vérifier si l'en-tête existe déjà ou s'il est restreint (WebHeaderCollection.IsRestricted(key)) avant d'appeler add

177
dubi

J'ai rencontré ce problème avec un client Web personnalisé. Je pense que les gens peuvent être déroutés à cause de multiples façons de le faire. Lorsque vous utilisez WebRequest.Create(), vous pouvez transtyper en HttpWebRequest et utiliser la propriété pour ajouter ou modifier un en-tête. Lorsque vous utilisez un WebHeaderCollection, vous pouvez utiliser le .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();
71
Chmod

Toutes les réponses précédentes décrivent le problème sans fournir de solution. Voici une méthode d'extension qui résout le problème en vous permettant de définir n'importe quel en-tête via son nom de chaîne.

Utilisation

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

Classe d'extension

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

Scénarios

J'ai écrit un wrapper pour HttpWebRequest et je ne voulais pas exposer les 13 en-têtes restreints en tant que propriétés dans mon wrapper. Au lieu de cela, je voulais utiliser un simple Dictionary<string, string>.

Un autre exemple est un proxy HTTP dans lequel vous devez prendre des en-têtes dans une demande et les transférer au destinataire.

Il y a beaucoup d'autres scénarios où il est tout simplement impossible ou possible d'utiliser des propriétés. Forcer l’utilisateur à définir l’en-tête via une propriété est une conception très rigide, c’est pourquoi une réflexion est nécessaire. L'aspect positif est que la réflexion est abstraite, qu'elle est toujours rapide (0,001 seconde dans mes tests) et qu'une méthode d'extension semble naturelle.

Notes

Les noms d'en-tête ne respectent pas la casse selon le RFC, http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2

28
Despertar

Chaque fois que vous modifiez les en-têtes d'un HttpWebRequest, vous devez utiliser les propriétés appropriées sur l'objet même, si elles existent. Si vous avez un WebRequest simple, assurez-vous de le convertir en HttpWebRequest en premier. Ensuite, Referrer dans votre cas peut être accédé via ((HttpWebRequest)request).Referrer, Vous n'avez donc pas besoin de modifier l'en-tête directement, il vous suffit de définir la propriété sur la bonne valeur. ContentLength, ContentType, UserAgent, etc., doivent tous être définis de cette façon.

IMHO, c'est une lacune sur la partie MS ... définir les en-têtes via Headers.Add() devrait automatiquement appeler la propriété appropriée en coulisse, si c'est ce qu'ils veulent faire.

12
jvenema

J'ai eu la même exception lorsque mon code a essayé de définir la valeur d'en-tête "Accept" comme ceci:

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

La solution était de changer cela en ceci:

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

WebRequest étant abstrait (et puisque toute classe héritante doit remplacer la propriété Headers) .. quelle WebRequest concrète utilisez-vous? En d'autres termes, comment obtenez-vous cet objet WebRequest?

ehr .. mnour answer m'a fait comprendre que le message d'erreur que vous obteniez est en fait sur place: il vous dit que l'en-tête que vous essayez d'ajouter existe déjà et que vous devez ensuite modifier sa valeur à l'aide de la propriété appropriée (l'indexeur, par exemple). ), au lieu d'essayer de l'ajouter à nouveau. C'est probablement tout ce que vous cherchiez.

D'autres classes héritant de WebRequest pourraient avoir des propriétés encore meilleures enveloppant certains en-têtes; Voir ce post par exemple.

7
FOR

Les réponses ci-dessus sont correctes, mais l'essentiel du problème réside dans le fait que certains en-têtes sont définis dans un sens et d'autres dans d'autres. Voir ci-dessus pour les listes 'en-tête restreint'. Pour ceux-ci, vous venez de les définir comme une propriété. Pour d'autres, vous ajoutez en fait l'en-tête. Vois ici.

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

    request.Accept = "application/json";

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

En gros non. C’est un en-tête http, il est donc raisonnable de convertir en HttpWebRequest et de définir le .Referer _ (comme vous l'indiquez dans la question):

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

J'utilise juste:

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

Vous pouvez simplement convertir WebRequest en un HttpWebRequest présenté ci-dessous:

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

et ensuite, au lieu d'essayer de manipuler la liste d'en-têtes, appliquez-la directement dans la propriété de requête request.Referer:

request.Referer = "yourReferer";

Ces propriétés sont disponibles dans l'objet de requête.

0
Bonomi