ไม่สามารถตั้งค่าส่วนหัว HTTP บางส่วนเมื่อใช้ System.Net.WebRequest


130

เมื่อฉันพยายามเพิ่มคู่คีย์ / ค่าส่วนหัว HTTP บนWebRequestวัตถุฉันได้รับข้อยกเว้นดังต่อไปนี้:

ต้องแก้ไขส่วนหัวนี้โดยใช้คุณสมบัติที่เหมาะสม

ฉันได้ลองเพิ่มค่าใหม่ให้กับHeadersคอลเล็กชันโดยใช้เมธอด Add () แต่ฉันยังคงได้รับข้อยกเว้นเหมือนเดิม

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

ฉันสามารถแก้ไขสิ่งนี้ได้โดยการแคสต์อ็อบเจ็กต์ WebRequest ไปยัง HttpWebRequest และตั้งค่าคุณสมบัติเช่นhttpWebReq.Referer ="http://stackoverflow.com"แต่ใช้ได้กับส่วนหัวไม่กี่ส่วนที่เปิดเผยผ่านคุณสมบัติ

ฉันต้องการทราบว่ามีวิธีควบคุมการปรับเปลี่ยนส่วนหัวด้วยการร้องขอทรัพยากรระยะไกลได้หรือไม่

คำตอบ:


182

หากคุณต้องการคำตอบสั้น ๆ และมีเทคนิคให้ไปที่ส่วนสุดท้ายของคำตอบ

ถ้าคุณอยากรู้มากขึ้นอ่านมันทั้งหมดและฉันหวังว่าคุณจะสนุกกับ ...


วันนี้ฉันก็ตอบโต้ปัญหานี้เช่นกันและสิ่งที่ฉันค้นพบในวันนี้ก็คือ:

  1. คำตอบข้างต้นเป็นจริงดัง:

    1.1 มีการแจ้งให้คุณทราบว่าส่วนหัวที่คุณพยายามเพิ่มมีอยู่แล้วและคุณควรแก้ไขค่าโดยใช้คุณสมบัติที่เหมาะสม (เช่นตัวสร้างดัชนี) แทนที่จะพยายามเพิ่มอีกครั้ง

    1.2 ทุกครั้งที่คุณเปลี่ยนส่วนหัวของ an HttpWebRequestคุณจำเป็นต้องใช้คุณสมบัติที่เหมาะสมกับวัตถุนั้นเองหากมีอยู่

ขอบคุณสำหรับและ Jvenema สำหรับแนวทางชั้นนำ ...

  1. แต่สิ่งที่ฉันค้นพบและนั่นคือชิ้นส่วนที่ขาดหายไปในปริศนาคือ:

    2.1 WebHeaderCollectionโดยทั่วไปชั้นเรียนสามารถเข้าถึงได้ผ่าน. WebRequestHeaders หรือ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)) ก่อนเรียกเพิ่ม


8
"แก้ไขค่าโดยใช้คุณสมบัติที่เหมาะสม" กล่าวไว้ทั้งหมด
CRice

76
คำตอบนี้เป็นเพียงการทำซ้ำข้อความของข้อยกเว้นโดยไม่ให้วิธีแก้ปัญหา
000

11
วิธีแก้ไขคือตรวจสอบว่ามีส่วนหัวอยู่แล้วหรือถูก จำกัด (WebHeaderCollection.IsRestricted (คีย์)) ก่อนที่จะเรียก add
Kaido

7
@Sam อ่านหัวข้อ 1.1 ซึ่งช่วยแก้ปัญหาได้ นั่นหมายความว่าคุณสมบัติที่เราพยายามเพิ่มผ่านHeaders.Add()มีอยู่แล้วดังนั้นเราจึงควรแก้ไขแทน
Junaid Qadir

4
"ฉันรู้สึกว่าเป็นเรื่องสำคัญที่จะต้องชี้ให้เห็นว่าข้อ จำกัด นี้เป็นคุณสมบัติของ. NET Framework" - ฉันไม่อยากมีคุณสมบัติแบบนี้
Herberth Amaral

77

ฉันพบปัญหานี้กับเว็บไคลเอ็นต์ที่กำหนดเอง ฉันคิดว่าผู้คนอาจสับสนเนื่องจากมีหลายวิธีในการทำเช่นนี้ เมื่อใช้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();

1
Ex 1 แก้ไขปัญหาของฉันด้วยข้อยกเว้นนี้ ฉันจึงเปลี่ยน client.Headers ["referer"] = url; ไปยัง client.Headers.Add ("referer", url); และสิ่งต่างๆได้ผล ขอบคุณ
000

2
ระวังว่าคำตอบนี้มีข้อสันนิษฐานที่น่ายินดีว่าคุณกำลังทำงานบนเดสก์ท็อปรันไทม์. Net และขอ http WebRequest.Create สามารถส่งคืนอ็อบเจ็กต์ต่างๆได้หลากหลายขึ้นอยู่กับคำนำหน้าโปรโตคอลที่คุณใช้ มันเกี่ยวข้องกับ CustomProtocolHandlers หากใครสนใจพวกเขา .. และใน WP7 หรือ Silverlight คลาสการใช้งานคำขอก็แตกต่างกันเล็กน้อยเช่นกัน ระวังแค่นี้ด้วย
quetzalcoatl

1
แต่ฉันไม่สามารถแก้ไขส่วนหัว "ยอมรับ" ได้ ฉันจะแก้ไขสิ่งนี้ได้อย่างไร?
ผู้ใช้

ตัวอย่างแรกยังคงให้ข้อผิดพลาดเดิมแก่ฉัน
mrid

29

คำตอบก่อนหน้านี้ทั้งหมดอธิบายถึงปัญหาโดยไม่ต้องให้คำตอบ นี่คือวิธีการขยายที่แก้ปัญหาโดยให้คุณตั้งค่าส่วนหัวผ่านชื่อสตริง

การใช้

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


ฉันใช้มันสำหรับ Proxy-Connection แต่หลังจากที่มันบอกว่าใช่ฉันมีคีย์สำหรับ "Proxy-Connection" มันส่งคืนค่าว่างซึ่งนำไปสู่ข้อยกเว้นการอ้างอิงที่เป็นโมฆะ
deadManN

ขอบคุณสำหรับการแก้ไขที่ชาญฉลาด ฉันปล่อยให้ส่วนขยายตั้งค่าส่วนหัวทั้งหมด: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

ฉันมีข้อยกเว้นเหมือนกันเมื่อรหัสของฉันพยายามตั้งค่าส่วนหัว "ยอมรับ" ดังนี้:

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

12

ทุกครั้งที่คุณเปลี่ยนส่วนหัวของ an HttpWebRequestคุณจำเป็นต้องใช้คุณสมบัติที่เหมาะสมกับวัตถุนั้นเองหากมีอยู่ หากคุณมีแบบธรรมดาWebRequestอย่าลืมแคสเป็นHttpWebRequestคนแรก จากนั้นReferrerในกรณีของคุณสามารถเข้าถึงได้((HttpWebRequest)request).Referrerดังนั้นคุณไม่จำเป็นต้องแก้ไขส่วนหัวโดยตรงเพียงแค่ตั้งค่าคุณสมบัติให้ถูกต้อง ContentLength, ContentType, UserAgentฯลฯ ทุกความต้องการที่จะตั้งด้วยวิธีนี้

IMHO นี่เป็นข้อบกพร่องในส่วนของ MS ... การตั้งค่าส่วนหัวผ่านHeaders.Add()ควรเรียกคุณสมบัติที่เหมาะสมเบื้องหลังโดยอัตโนมัติหากนั่นคือสิ่งที่พวกเขาต้องการทำ


7

WebRequestเป็นนามธรรม (และเนื่องจากคลาสที่สืบทอดใด ๆ ต้องแทนที่คุณสมบัติส่วนหัว) .. คุณกำลังใช้ WebRequest แบบใด กล่าวอีกนัยหนึ่งคุณจะทำให้ออบเจ็กต์ WebRequest นั้นเข้ากันได้อย่างไร?

ehr .. mnour คำตอบทำให้ฉันรู้ว่าข้อความแสดงข้อผิดพลาดที่คุณได้รับนั้นแท้จริงแล้วมันบอกคุณว่าส่วนหัวที่คุณพยายามจะเพิ่มนั้นมีอยู่แล้วและคุณควรแก้ไขค่าโดยใช้คุณสมบัติที่เหมาะสม (เช่นตัวสร้างดัชนี ) แทนที่จะพยายามเพิ่มอีกครั้ง นั่นอาจเป็นทั้งหมดที่คุณกำลังมองหา

คลาสอื่น ๆ ที่สืบทอดมาจาก WebRequest อาจมีคุณสมบัติที่ดีกว่าในการตัดส่วนหัวบางส่วน ดูโพสต์นี้เช่น


จริงๆแล้ว WebRequest.Create (url) จะสร้างอินสแตนซ์ของวัตถุ WebRequest
Igal Tabachnik

2

คำตอบข้างต้นนั้นใช้ได้ดี แต่สาระสำคัญของปัญหาคือส่วนหัวบางส่วนถูกตั้งค่าไว้ทางเดียวและบางส่วนกำหนดไว้เป็นวิธีอื่น ดูรายการ "ส่วนหัวที่ จำกัด " ด้านบน สำหรับสิ่งเหล่านี้คุณเพียงแค่ตั้งค่าเป็นคุณสมบัติ สำหรับคนอื่นคุณเพิ่มส่วนหัวจริงๆ ดูที่นี่.

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

    request.Accept = "application/json";

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

1

โดยทั่วไปไม่มี นั่นคือส่วนหัว http ดังนั้นจึงสมเหตุสมผลที่จะส่งHttpWebRequestและตั้งค่า.Referer(ตามที่คุณระบุในคำถาม):

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

1

หมายเหตุ: โซลูชันนี้จะทำงานร่วมกับ WebClientSocket เช่นเดียวกับ HttpWebRequest หรือคลาสอื่น ๆ ที่ใช้ WebHeaderCollection เพื่อทำงานกับส่วนหัว

หากคุณดูซอร์สโค้ดของ WebHeaderCollection.cs คุณจะเห็นว่า Hinfo ถูกใช้เพื่อเก็บข้อมูลของส่วนหัวที่รู้จักทั้งหมด:

private static readonly HeaderInfoTable HInfo = new HeaderInfoTable();

เมื่อดูที่คลาส HeaderInfoTable คุณจะสังเกตเห็นว่าข้อมูลทั้งหมดถูกเก็บไว้ในตารางแฮช

private static Hashtable HeaderHashTable;

นอกจากนี้ในตัวควบคุมแบบคงที่ของ HeaderInfoTable คุณจะเห็นส่วนหัวที่รู้จักทั้งหมดถูกเพิ่มในอาร์เรย์ HeaderInfo จากนั้นคัดลอกไปยังแฮชแท็ก

รูปลักษณ์สุดท้ายที่คลาส HeaderInfo แสดงชื่อของเขตข้อมูล

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

ดังนั้นจากทั้งหมดข้างต้นนี่คือรหัสที่ใช้การสะท้อนเพื่อค้นหา Hashtable แบบคงที่ในคลาส HeaderInfoTable จากนั้นเปลี่ยน HeaderInfo ที่ จำกัด การร้องขอทุกรายการภายในตารางแฮชให้ไม่ถูก จำกัด

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

                        }
                    }

                }
            }
        } 

ยอดเยี่ยม! นอกจากนี้ยังช่วยให้สามารถตั้งค่าส่วนหัวเหล่านั้นสำหรับคำขอที่ใช้เมื่อตั้งค่าเว็บซ็อกเก็ตและด้วยเหตุนี้การแก้ไขปัญหานี้: github.com/dotnet/corefx/issues/26627
Øystein Kolsrud

นั่นควรเป็นเช่นนั้นเพราะพวกเขาทั้งหมดใช้ WebHeaderCollection เพื่อจัดการส่วนหัว ฉันได้ทดสอบบน HttpWebRequest เท่านั้น
ตู้นอน


0

คุณสามารถส่ง WebRequest ไปยัง HttpWebRequest ที่แสดงด้านล่าง:

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

จากนั้นแทนที่จะพยายามจัดการกับรายการส่วนหัวให้นำไปใช้โดยตรงในคำร้องขอคุณสมบัติผู้อ้างอิง:

request.Referer = "yourReferer";

คุณสมบัติเหล่านี้มีอยู่ในอ็อบเจ็กต์คำร้องขอ

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.