Bei Verwendung von System.Net.WebRequest können einige HTTP-Header nicht festgelegt werden

129

Wenn ich versuche, einem WebRequestObjekt ein HTTP-Header-Schlüssel / Wert-Paar hinzuzufügen , wird die folgende Ausnahme angezeigt:

Dieser Header muss mit der entsprechenden Eigenschaft geändert werden

Ich habe versucht, der HeadersSammlung mithilfe der Add () -Methode neue Werte hinzuzufügen, erhalte jedoch immer noch dieselbe Ausnahme.

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

Ich kann dies umgehen, indem ich das WebRequest-Objekt in eine HttpWebRequest umwandle und die Eigenschaften wie z. B. festlege. httpWebReq.Referer ="http://stackoverflow.com"Dies funktioniert jedoch nur für eine Handvoll Header, die über Eigenschaften verfügbar gemacht werden.

Ich würde gerne wissen, ob es eine Möglichkeit gibt, die Änderung von Headern mit der Anforderung einer Remote-Ressource genauer zu steuern.

Rasierer
quelle

Antworten:

182

Wenn Sie eine kurze und technische Antwort benötigen, fahren Sie mit dem letzten Abschnitt der Antwort fort.

Wenn Sie es besser wissen wollen, lesen Sie alles und ich hoffe, Sie werden es genießen ...


Ich habe diesem Problem auch heute begegnet, und was ich heute entdeckt habe, ist Folgendes:

  1. Die obigen Antworten sind wahr als:

    1.1 Es sagt Ihnen, dass der Header, den Sie hinzufügen möchten, bereits vorhanden ist, und Sie sollten seinen Wert dann mithilfe der entsprechenden Eigenschaft (z. B. des Indexers) ändern, anstatt erneut zu versuchen, ihn hinzuzufügen.

    1.2 Jedes Mal HttpWebRequest, wenn Sie die Header von ändern , müssen Sie die entsprechenden Eigenschaften für das Objekt selbst verwenden, falls vorhanden.

Vielen Dank für und Jvenema für die führenden Richtlinien ...

  1. Aber was ich herausgefunden habe und das war das fehlende Teil im Puzzle ist:

    2.1 Auf die WebHeaderCollectionKlasse wird im Allgemeinen über WebRequest.Headers oder WebResponse.Headers zugegriffen. Einige häufig verwendete Header gelten als eingeschränkt und werden entweder direkt von der API (z. B. Content-Type) verfügbar gemacht oder vom System geschützt und können nicht geändert werden.

Die eingeschränkten Header sind:

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

Wenn Sie das nächste Mal mit dieser Ausnahme konfrontiert sind und nicht wissen, wie Sie sie lösen sollen, denken Sie daran, dass es einige eingeschränkte Header gibt. Die Lösung besteht darin, ihre Werte mithilfe der entsprechenden Eigenschaft explizit aus der Klasse WebRequest/ zu ändern HttpWebRequest.


Bearbeiten: (nützlich, aus Kommentaren, Kommentar von Benutzer Kaido )

Die Lösung besteht darin, WebHeaderCollection.IsRestricted(key)vor dem Aufruf von add zu überprüfen, ob der Header bereits vorhanden oder eingeschränkt ist ( )

Dubi
quelle
8
"Ändern Sie ihre Werte mit der entsprechenden Eigenschaft" sagt alles
CRice
76
Diese Antwort wiederholt lediglich die Nachricht der Ausnahmen, ohne eine Lösung für das Problem zu geben.
000
11
Die Lösung besteht darin, zu überprüfen, ob der Header bereits vorhanden oder eingeschränkt ist (WebHeaderCollection.IsRestricted (Schlüssel)), bevor Sie add aufrufen
Kaido
7
@ Sam las Abschnitt 1.1, der das Problem löst. Das bedeutet, dass die Eigenschaft, über die wir hinzufügen möchten, Headers.Add()bereits vorhanden ist. Daher sollten wir sie stattdessen ändern.
Junaid Qadir
4
"Ich halte es für wichtig, darauf hinzuweisen, dass diese Einschränkung eine Funktion von .NET Framework ist" - ich hätte diese Art von Funktion lieber nicht.
Herberth Amaral
76

Ich bin auf dieses Problem mit einem benutzerdefinierten Webclient gestoßen. Ich denke, die Leute könnten verwirrt sein, weil es mehrere Möglichkeiten gibt, dies zu tun. Bei Verwendung können WebRequest.Create()Sie HttpWebRequesteinen Header umwandeln und mithilfe der Eigenschaft einen Header hinzufügen oder ändern. Wenn WebHeaderCollectionSie ein verwenden , können Sie das verwenden.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();
Chmod
quelle
1
Der Ex 1 hat mein Problem mit dieser Ausnahme gelöst. Also habe ich client.Headers ["referer"] = url geändert; an client.Headers.Add ("Referer", URL); und die Dinge funktionieren. Vielen Dank.
000
2
Beachten Sie, dass diese Antwort eine glückliche Annahme enthält, dass Sie an der Desktop .Net-Laufzeit arbeiten und nach http fragen. WebRequest.Create kann je nach verwendetem Protokollpräfix verschiedene Objekte zurückgeben. Es hängt mit CustomProtocolHandlers zusammen, wenn sich jemand für sie interessiert. Und in WP7 oder Silverlight unterscheiden sich auch die Anforderungsimplementierungsklassen ein wenig. Sei einfach vorsichtig damit.
Quetzalcoatl
1
Ich kann den Header "Akzeptieren" jedoch nicht ändern. Wie kann ich das ändern?
Benutzer
Das erste Beispiel gibt mir immer noch den gleichen Fehler
Mrid
29

Alle vorherigen Antworten beschreiben das Problem, ohne eine Lösung bereitzustellen. Hier ist eine Erweiterungsmethode, die das Problem löst, indem Sie einen beliebigen Header über seinen Zeichenfolgennamen festlegen können.

Verwendung

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

Erweiterungsklasse

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

Szenarien

Ich habe einen Wrapper für geschrieben HttpWebRequestund wollte nicht alle 13 eingeschränkten Header als Eigenschaften in meinem Wrapper verfügbar machen. Stattdessen wollte ich eine einfache verwenden Dictionary<string, string>.

Ein weiteres Beispiel ist ein HTTP-Proxy, bei dem Sie Header in einer Anforderung aufnehmen und an den Empfänger weiterleiten müssen.

Es gibt viele andere Szenarien, in denen es einfach nicht praktikabel oder möglich ist, Eigenschaften zu verwenden. Den Benutzer zu zwingen, den Header über eine Eigenschaft zu setzen, ist ein sehr unflexibles Design, weshalb Reflexion erforderlich ist. Der Vorteil ist, dass die Reflexion weg abstrahiert wird, immer noch schnell ist (0,001 Sekunden in meinen Tests) und sich als Erweiterungsmethode natürlich anfühlt.

Anmerkungen

Bei Headernamen wird gemäß RFC ( http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2) die Groß- und Kleinschreibung nicht berücksichtigt

Despertar
quelle
Ich benutze es für Proxy-Connection, aber nachdem es gesagt hat, ja, ich habe den Schlüssel für "Proxy-Connection", es gibt null zurück, was zu einer Null-Referenz-Ausnahme führt
deadManN
Vielen Dank für die clevere Lösung. Ich ließ die Erweiterung alle Header setzen: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

Ich hatte die gleiche Ausnahme, als mein Code versuchte, den Header-Wert "Accept" wie folgt festzulegen:

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

Die Lösung bestand darin, dies zu ändern:

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

Jedes Mal HttpWebRequest, wenn Sie die Header von ändern , müssen Sie die entsprechenden Eigenschaften für das Objekt selbst verwenden, sofern diese vorhanden sind. Wenn Sie eine Ebene haben WebRequest, stellen Sie sicher, dass Sie eine HttpWebRequesterste verwenden. Dann Referrerin Ihrem Fall kann abgerufen werden ((HttpWebRequest)request).Referrer, so dass Sie nicht den Header müssen direkt ändern - nur die Eigenschaft auf den richtigen Wert gesetzt. ContentLength, ContentType, UserAgent, Usw, alle müssen auf diese Weise eingestellt werden.

IMHO, dies ist ein Mangel für MS-Teil ... das Setzen der Header über Headers.Add()sollte automatisch die entsprechende Eigenschaft hinter den Kulissen aufrufen, wenn sie dies tun möchten.

jvenema
quelle
7

WebRequest ist abstrakt (und da jede erbende Klasse die Headers-Eigenschaft überschreiben muss). Welche konkrete WebRequest verwenden Sie? Mit anderen Worten, wie erhalten Sie das WebRequest-Objekt, mit dem Sie übereinstimmen?

ehr .. mnour Antwort hat mir klar gemacht, dass die Fehlermeldung, die Sie erhalten haben, genau richtig ist: Sie sagt Ihnen, dass der Header, den Sie hinzufügen möchten, bereits vorhanden ist, und Sie sollten seinen Wert dann mithilfe der entsprechenden Eigenschaft (z. B. des Indexers) ändern ), anstatt zu versuchen, es erneut hinzuzufügen. Das ist wahrscheinlich alles, wonach Sie gesucht haben.

Andere Klassen, die von WebRequest erben, haben möglicherweise noch bessere Eigenschaften, die bestimmte Header umschließen. Siehe diesen Beitrag zum Beispiel.

ZUM
quelle
Tatsächlich erstellt WebRequest.Create (url) eine Instanz eines WebRequest-Objekts.
Igal Tabachnik
2

Die obigen Antworten sind alle in Ordnung, aber das Wesentliche des Problems ist, dass einige Header in eine Richtung und andere in eine andere Richtung gesetzt werden. Siehe oben für 'eingeschränkte Header'-Listen. Für diese legen Sie sie einfach als Eigenschaft fest. Für andere fügen Sie tatsächlich die Kopfzeile hinzu. Siehe hier.

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

    request.Accept = "application/json";

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

Grundsätzlich nein. Da es sich um einen http-Header handelt, ist es sinnvoll, Folgendes zu ändern HttpWebRequestund festzulegen .Referer(wie Sie in der Frage angeben):

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

Hinweis: Diese Lösung funktioniert sowohl mit WebClientSocket als auch mit HttpWebRequest oder einer anderen Klasse, die WebHeaderCollection verwendet, um mit Headern zu arbeiten.

Wenn Sie sich den Quellcode von WebHeaderCollection.cs ansehen, werden Sie feststellen, dass Hinfo verwendet wird, um Informationen zu allen bekannten Headern zu speichern:

private static readonly HeaderInfoTable HInfo = new HeaderInfoTable();

Wenn Sie sich die HeaderInfoTable-Klasse ansehen, können Sie feststellen, dass alle Daten in einer Hash-Tabelle gespeichert sind

private static Hashtable HeaderHashTable;

Außerdem können Sie im statischen Konstruktor von HeaderInfoTable sehen, dass alle bekannten Header im HeaderInfo-Array hinzugefügt und dann in die Hashtabelle kopiert werden.

Der letzte Blick auf die HeaderInfo-Klasse zeigt die Namen der Felder.

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

Bei all dem ist hier ein Code, der mithilfe von Reflektion statische Hashtable in der HeaderInfoTable-Klasse findet und dann jede anforderungsbeschränkte HeaderInfo in der Hash-Tabelle so ändert, dass sie nicht eingeschränkt wird

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

                        }
                    }

                }
            }
        } 
Schläfer
quelle
Brillant! Dies ermöglicht es auch, diese Header für die Anforderung festzulegen, die beim Einrichten von Web-Sockets verwendet wird, und damit dieses Problem zu umgehen
Øystein Kolsrud
Dies sollte der Fall sein, da alle WebHeaderCollection verwenden, um Header zu bearbeiten. Ich habe es nur auf HttpWebRequest getestet.
Sleeper
0

Ich benutze nur:

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

Sie können die WebRequest einfach in eine unten gezeigte HttpWebRequest umwandeln:

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

und anstatt zu versuchen, die Header-Liste zu manipulieren, wenden Sie sie direkt in der Anfrage-Eigenschaftsanforderung an. Referenz:

request.Referer = "yourReferer";

Diese Eigenschaften sind im Anforderungsobjekt verfügbar.

Bonomi
quelle