Impossibile impostare alcune intestazioni HTTP quando si utilizza System.Net.WebRequest


130

Quando provo ad aggiungere una coppia chiave / valore dell'intestazione HTTP su un WebRequestoggetto, ottengo la seguente eccezione:

Questa intestazione deve essere modificata utilizzando la proprietà appropriata

Ho provato ad aggiungere nuovi valori alla Headersraccolta utilizzando il metodo Add () ma ho ancora la stessa eccezione.

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

Posso evitarlo lanciando l'oggetto WebRequest su un HttpWebRequest e impostando proprietà come httpWebReq.Referer ="http://stackoverflow.com", ma questo funziona solo per una manciata di intestazioni che sono esposte tramite proprietà.

Mi piacerebbe sapere se esiste un modo per ottenere un controllo più preciso sulla modifica delle intestazioni con una richiesta per una risorsa remota.

Risposte:


182

Se hai bisogno di una risposta breve e tecnica vai direttamente all'ultima sezione della risposta.

Se vuoi conoscere meglio, leggi tutto e spero che ti piacerà ...


Ho contrastato anche questo problema oggi, e quello che ho scoperto oggi è che:

  1. le risposte sopra sono vere, come:

    1.1 ti dice che l'intestazione che stai tentando di aggiungere esiste già e dovresti quindi modificarne il valore usando la proprietà appropriata (l'indicizzatore, ad esempio), invece di provare ad aggiungerla di nuovo.

    1.2 Ogni volta che si modificano le intestazioni di un HttpWebRequest, è necessario utilizzare le proprietà appropriate sull'oggetto stesso, se esistenti.

Grazie a FOR e Jvenema per le linee guida principali ...

  1. Ma quello che ho scoperto e che era il pezzo mancante nel puzzle è che:

    2.1 La WebHeaderCollectionclasse è generalmente accessibile tramite WebRequest.Headers o WebResponse.Headers. Alcune intestazioni comuni sono considerate limitate e sono esposte direttamente dall'API (come Content-Type) o protette dal sistema e non possono essere modificate.

Le intestazioni limitate sono:

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

Quindi, la prossima volta che stai affrontando questa eccezione e non sai come risolverlo, ricorda che ci sono alcune intestazioni limitate e la soluzione è quella di modificare i loro valori usando esplicitamente la proprietà appropriata dalla classe WebRequest/ HttpWebRequest.


Modifica: (utile, dai commenti, commento dell'utente Kaido )

La soluzione è verificare se l'intestazione esiste già o è limitata ( WebHeaderCollection.IsRestricted(key)) prima di chiamare add


8
"modifica i loro valori usando la proprietà appropriata" dice tutto
CRice

76
Questa risposta sta semplicemente ripetendo il Messaggio delle eccezioni senza fornire una soluzione al problema.
000,

11
La soluzione è verificare se l'intestazione esiste già o è limitata (WebHeaderCollection.IsRestricted (chiave)) prima di chiamare add
Kaido

7
@Sam leggi la sezione 1.1 che risolve il problema. ciò significa che la proprietà che stiamo tentando di aggiungere tramite Headers.Add()esiste già, pertanto dovremmo modificarla.
Junaid Qadir,

4
"Ritengo sia importante sottolineare che questa limitazione è una funzionalità di .NET Framework" - Preferirei non avere questo tipo di funzionalità.
Herberth Amaral,

76

Ho riscontrato questo problema con un client Web personalizzato. Penso che le persone possano essere confuse a causa di molteplici modi per farlo. Quando si utilizza WebRequest.Create()è possibile eseguire il cast in an HttpWebRequeste utilizzare la proprietà per aggiungere o modificare un'intestazione. Quando si utilizza un WebHeaderCollectionè possibile utilizzare il .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();

1
L'Ex 1 ha risolto il mio problema con questa eccezione. Quindi ho cambiato client.Headers ["referer"] = url; a client.Headers.Add ("referer", url); e le cose funzionano. Grazie.
000,

2
attenzione che questa risposta contiene un presupposto felice di lavorare sul runtime desktop .Net e di chiedere http. WebRequest.Create può restituire una varietà di oggetti diversi a seconda del prefisso del protocollo utilizzato. È correlato a CustomProtocolHandlers se qualcuno è interessato a loro .. E su WP7 o Silverlight anche le classi di implementazione della richiesta sono leggermente diverse. Stai solo attento con questo.
quetzalcoatl,

1
Ma non posso modificare l'intestazione "Accetta". Come posso modificarlo?
utente

Il primo esempio mi sta ancora dando lo stesso errore
mrid

29

Tutte le risposte precedenti descrivono il problema senza fornire una soluzione. Ecco un metodo di estensione che risolve il problema consentendo di impostare qualsiasi intestazione tramite il nome della stringa.

uso

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

Classe di estensione

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

scenari

Ho scritto un wrapper per HttpWebRequeste non volevo esporre tutte e 13 le intestazioni limitate come proprietà nel mio wrapper. Invece volevo usare un semplice Dictionary<string, string>.

Un altro esempio è un proxy HTTP in cui è necessario prendere le intestazioni in una richiesta e inoltrarle al destinatario.

Ci sono molti altri scenari in cui non è solo pratico o possibile usare le proprietà. Costringere l'utente a impostare l'intestazione tramite una proprietà è un progetto molto flessibile, motivo per cui è necessaria una riflessione. Il lato positivo è che il riflesso viene sottratto, è ancora veloce (0,001 secondi nei miei test) e come metodo di estensione sembra naturale.

Appunti

I nomi delle intestazioni non fanno distinzione tra maiuscole e minuscole per RFC, http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2


lo uso per Proxy-Connection, ma dopo averlo detto, sì, contengo la chiave per "Proxy-Connection" che restituisce null, che porta a un'eccezione di riferimento null
deadManN

Grazie per la correzione intelligente. Lascio che l'estensione abbia impostato tutte le intestazioni: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

Ho avuto la stessa eccezione quando il mio codice ha cercato di impostare il valore dell'intestazione "Accetta" in questo modo:

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

La soluzione era cambiarla in questo:

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

12

Ogni volta che si modificano le intestazioni di un HttpWebRequest, è necessario utilizzare le proprietà appropriate sull'oggetto stesso, se esistenti. Se hai una pianura WebRequest, assicurati di lanciarla per HttpWebRequestprima. Quindi, Referrernel tuo caso, è possibile accedervi tramite ((HttpWebRequest)request).Referrer, quindi non è necessario modificare direttamente l'intestazione: basta impostare la proprietà sul valore corretto. ContentLength, ContentType, UserAgent, Ecc, tutti hanno bisogno di essere impostato in questo modo.

IMHO, questo è un difetto nella parte MS ... l'impostazione delle intestazioni tramite Headers.Add()dovrebbe chiamare automaticamente la proprietà appropriata dietro le quinte, se è quello che vogliono fare.


7

Essendo WebRequest astratto (e poiché qualsiasi classe ereditaria deve sovrascrivere la proprietà Headers) .. quale WebRequest concreto stai usando? In altre parole, come si ottiene quell'oggetto WebRequest con cui si deve firmare?

ehr .. mnour answer mi ha fatto capire che il messaggio di errore che stavi ricevendo in realtà è perfetto: ti sta dicendo che l'intestazione che stai cercando di aggiungere già esiste e dovresti quindi modificarne il valore usando la proprietà appropriata (l'indicizzatore, ad esempio ), invece di provare ad aggiungerlo di nuovo. Questo è probabilmente tutto quello che stavi cercando.

Altre classi che ereditano da WebRequest potrebbero avere proprietà ancora migliori che racchiudono determinate intestazioni; Vedi questo post per esempio.


In realtà WebRequest.Create (url) crea un'istanza di un oggetto WebRequest.
Igal Tabachnik,

2

Le risposte di cui sopra vanno bene, ma l'essenza del problema è che alcune intestazioni sono impostate in un modo e altri in altri. Vedi sopra per gli elenchi di "intestazione riservata". Per questi, li hai appena impostati come proprietà. Per altri, in realtà aggiungi l'intestazione. Vedere qui.

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

    request.Accept = "application/json";

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

1

Fondamentalmente no. Questa è un'intestazione http, quindi è ragionevole eseguire il cast HttpWebRequeste impostare il .Referer(come indicato nella domanda):

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

1

Nota: questa soluzione funzionerà con WebClientSocket nonché con HttpWebRequest o qualsiasi altra classe che utilizza WebHeaderCollection per lavorare con le intestazioni.

Se guardi il codice sorgente di WebHeaderCollection.cs vedrai che Hinfo è usato per conservare le informazioni di tutte le intestazioni conosciute:

private static readonly HeaderInfoTable HInfo = new HeaderInfoTable();

Guardando la classe HeaderInfoTable, puoi notare che tutti i dati sono memorizzati nella tabella hash

private static Hashtable HeaderHashTable;

Inoltre, nel contratore statico di HeaderInfoTable, è possibile visualizzare tutte le intestazioni note aggiunte nell'array HeaderInfo e quindi copiate nell'hashtable.

Lo sguardo finale alla classe HeaderInfo mostra i nomi dei campi.

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

Quindi, con tutto quanto sopra, ecco un codice che utilizza la reflection per trovare Hashtable statico nella classe HeaderInfoTable e quindi cambia ogni tabella di hash HeaderInfo con restrizioni limitate per essere illimitata

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

                        }
                    }

                }
            }
        } 

Brillante! Ciò consente anche di impostare tali intestazioni per la richiesta utilizzata durante l'impostazione dei socket Web e quindi aggirare il problema: github.com/dotnet/corefx/issues/26627
Øystein Kolsrud

Questo dovrebbe essere il caso perché tutti usano WebHeaderCollection per manipolare le intestazioni. L'ho provato solo su HttpWebRequest.
Dormiente

0

Sto usando solo:

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

0

Puoi semplicemente trasmettere il WebRequest a un HttpWebRequest mostrato di seguito:

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

e quindi invece di provare a manipolare l'elenco di intestazioni, applicarlo direttamente nella richiesta di proprietà della richiesta.

request.Referer = "yourReferer";

Queste proprietà sono disponibili nell'oggetto richiesta.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.