Không thể đặt một số tiêu đề HTTP khi sử dụng System.Net.WebRequest


130

Khi tôi cố gắng thêm cặp khóa / giá trị HTTP trên một WebRequestđối tượng, tôi nhận được ngoại lệ sau:

Tiêu đề này phải được sửa đổi bằng cách sử dụng thuộc tính thích hợp

Tôi đã thử thêm các giá trị mới vào Headersbộ sưu tập bằng cách sử dụng phương thức Add () nhưng tôi vẫn nhận được ngoại lệ tương tự.

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

Tôi có thể khắc phục điều này bằng cách chuyển đối tượng WebRequest thành một httpWebRequest và thiết lập các thuộc tính như httpWebReq.Referer ="http://stackoverflow.com", nhưng điều này chỉ hoạt động đối với một số tiêu đề được hiển thị thông qua các thuộc tính.

Tôi muốn biết liệu có cách nào để có quyền kiểm soát chi tiết hơn đối với việc sửa đổi các tiêu đề với yêu cầu tài nguyên từ xa không.

Câu trả lời:


182

Nếu bạn cần câu trả lời ngắn và kỹ thuật, hãy đi thẳng đến phần cuối của câu trả lời.

Nếu bạn muốn biết rõ hơn, hãy đọc tất cả, và tôi hy vọng bạn sẽ thích ...


Tôi đã chống lại vấn đề này ngày hôm nay, và những gì tôi phát hiện ra ngày hôm nay là:

  1. các câu trả lời trên là đúng, như:

    1.1 nó cho bạn biết rằng tiêu đề bạn đang cố thêm đã tồn tại và sau đó bạn nên sửa đổi giá trị của nó bằng cách sử dụng thuộc tính thích hợp (ví dụ như bộ chỉ mục), thay vì cố gắng thêm lại.

    1.2 Bất cứ khi nào bạn thay đổi tiêu đề của một HttpWebRequest, bạn cần sử dụng các thuộc tính phù hợp trên chính đối tượng, nếu chúng tồn tại.

Cảm ơn FOR và Jvenema vì những hướng dẫn hàng đầu ...

  1. Nhưng, những gì tôi phát hiện ra, và đó là mảnh ghép còn thiếu trong câu đố là:

    2.1 WebHeaderCollectionLớp thường được truy cập thông qua WebRequest.Headers hoặc WebResponse.Headers. Một số tiêu đề phổ biến được coi là bị hạn chế và được API hiển thị trực tiếp (như Loại nội dung) hoặc được hệ thống bảo vệ và không thể thay đổi.

Các tiêu đề bị hạn chế là:

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

Vì vậy, lần tới khi bạn phải đối mặt với ngoại lệ này và không biết cách giải quyết vấn đề này, hãy nhớ rằng có một số tiêu đề bị hạn chế và giải pháp là sửa đổi giá trị của chúng bằng cách sử dụng thuộc tính thích hợp rõ ràng từ WebRequest/ HttpWebRequestclass.


Chỉnh sửa: (hữu ích, từ nhận xét, nhận xét của người dùng Kaido )

Giải pháp là kiểm tra xem tiêu đề đã tồn tại hay bị hạn chế ( WebHeaderCollection.IsRestricted(key)) trước khi gọi add


8
"sửa đổi giá trị của chúng bằng cách sử dụng thuộc tính phù hợp" nói lên tất cả
CRice

76
Câu trả lời này chỉ là lặp lại Thông điệp của ngoại lệ mà không đưa ra giải pháp cho vấn đề.
000

11
Giải pháp là kiểm tra xem tiêu đề đã tồn tại hay đã bị hạn chế (WebHeaderCollection.IsRestricted (khóa)) trước khi gọi add
Kaido

7
@Sam đọc phần 1.1 để giải quyết vấn đề. điều đó có nghĩa là tài sản chúng ta đang cố gắng thêm thông qua Headers.Add()đã tồn tại do đó chúng ta nên sửa đổi nó.
Qadir

4
"Tôi cảm thấy điều quan trọng là chỉ ra rằng hạn chế này là một tính năng của .NET Framework" - Tôi không muốn có loại tính năng này.
Herberth Amaral

76

Tôi gặp vấn đề này với một khách hàng web tùy chỉnh. Tôi nghĩ mọi người có thể bị nhầm lẫn vì nhiều cách để làm điều này. Khi sử dụng, WebRequest.Create()bạn có thể HttpWebRequestsử dụng và sử dụng thuộc tính để thêm hoặc sửa đổi tiêu đề. Khi sử dụng, WebHeaderCollectionbạn có thể sử dụng .Add("referer","my_url").

Vd 1

WebClient client = new WebClient();
client.Headers.Add("referer", "http://stackoverflow.com");
client.Headers.Add("user-agent", "Mozilla/5.0");

Vd 2

HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
request.Referer = "http://stackoverflow.com";
request.UserAgent = "Mozilla/5.0";
response = (HttpWebResponse)request.GetResponse();

1
Ex 1 đã giải quyết vấn đề của tôi với ngoại lệ này. Vì vậy, tôi đã thay đổi client.Headers ["tham chiếu"] = url; đến client.Headers.Add ("người giới thiệu", url); và mọi thứ đang làm việc. Cảm ơn.
000

2
hãy cẩn thận rằng câu trả lời này chứa một giả định vui rằng bạn đang làm việc trên máy tính để bàn .Net runtime và yêu cầu http. WebRequest.Create có thể trả về nhiều đối tượng khác nhau tùy thuộc vào tiền tố giao thức bạn sử dụng. Nó có liên quan đến CustomProtocolHandlers nếu có ai quan tâm đến họ .. Và trên WP7 hoặc Silverlight, các lớp thực hiện yêu cầu cũng có một chút khác biệt. Chỉ cần cẩn thận với điều này.
quetzalcoatl

1
Nhưng tôi không thể sửa đổi tiêu đề "Chấp nhận". Làm thế nào tôi có thể sửa đổi điều này?
người dùng

Ví dụ đầu tiên vẫn gây ra lỗi tương tự
mrid

29

Tất cả các câu trả lời trước mô tả vấn đề mà không cung cấp giải pháp. Đây là một phương thức mở rộng để giải quyết vấn đề bằng cách cho phép bạn đặt bất kỳ tiêu đề nào thông qua tên chuỗi của nó.

Sử dụng

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

Lớp học mở rộng

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

Kịch bản

Tôi đã viết một trình bao bọc cho HttpWebRequestvà không muốn để lộ tất cả 13 tiêu đề bị hạn chế làm thuộc tính trong trình bao bọc của mình. Thay vào đó tôi muốn sử dụng một cách đơn giản Dictionary<string, string>.

Một ví dụ khác là proxy HTTP, nơi bạn cần lấy các tiêu đề trong yêu cầu và chuyển tiếp chúng đến người nhận.

Có rất nhiều tình huống khác trong đó các thuộc tính của nó không thực tế hoặc có thể sử dụng. Buộc người dùng đặt tiêu đề thông qua một thuộc tính là một thiết kế rất không linh hoạt, đó là lý do tại sao cần phải có sự phản chiếu. Mặt trái là sự phản chiếu được trừu tượng hóa, nó vẫn nhanh (0,001 giây trong các thử nghiệm của tôi) và như một phương pháp mở rộng tạo cảm giác tự nhiên.

Ghi chú

Tên tiêu đề không phân biệt chữ hoa chữ thường theo RFC, http://www.w3.org/Prot Protocol / rfc2616 / rfc2616-sec4.html # sec4.2


Tôi sử dụng nó cho Proxy-Connection, nhưng sau khi nó nói, vâng tôi chứa khóa cho "Proxy-Connection", nó trả về null, dẫn đến ngoại lệ tham chiếu null
deadManN

Cảm ơn bạn đã sửa chữa thông minh. Tôi để tiện ích mở rộng đặt tất cả các tiêu đề: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

Tôi đã có ngoại lệ tương tự khi mã của tôi cố gắng đặt giá trị tiêu đề "Chấp nhận" như thế này:

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

Giải pháp là thay đổi nó thành thế này:

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

12

Bất cứ khi nào bạn thay đổi tiêu đề của một HttpWebRequest, bạn cần sử dụng các thuộc tính phù hợp trên chính đối tượng, nếu chúng tồn tại. Nếu bạn có một đồng bằng WebRequest, hãy chắc chắn để đặt nó lên HttpWebRequestđầu tiên. Sau đó, Referrertrong trường hợp của bạn có thể được truy cập qua ((HttpWebRequest)request).Referrer, vì vậy bạn không cần phải sửa đổi tiêu đề trực tiếp - chỉ cần đặt thuộc tính thành đúng giá trị. ContentLength, ContentType, UserAgent, Vv, tất cả cần phải được thiết lập theo cách này.

IMHO, đây là một thiếu sót trên phần MS ... thiết lập các tiêu đề thông qua Headers.Add()sẽ tự động gọi thuộc tính thích hợp phía sau hậu trường, nếu đó là điều họ muốn làm.


7

WebRequest là trừu tượng (và vì bất kỳ lớp kế thừa nào cũng phải ghi đè thuộc tính Tiêu đề) .. bạn đang sử dụng WebRequest cụ thể nào? Nói cách khác, làm thế nào để bạn có được đối tượng WebRequest phù hợp với?

ừm ), thay vì cố gắng thêm nó một lần nữa. Đó có lẽ là tất cả những gì bạn đang tìm kiếm.

Các lớp khác kế thừa từ WebRequest có thể có các thuộc tính tốt hơn bao bọc các tiêu đề nhất định; Xem bài này chẳng hạn.


Trên thực tế, WebRequest.Create (url) tạo một thể hiện của một đối tượng WebRequest.
Igal Tabachnik

2

Các câu trả lời trên đều ổn, nhưng bản chất của vấn đề là một số tiêu đề được đặt theo một cách và các tiêu đề khác được đặt theo các cách khác. Xem ở trên để biết danh sách 'tiêu đề bị hạn chế'. Hoặc là những thứ này, bạn chỉ cần đặt chúng làm tài sản. Đối với những người khác, bạn thực sự thêm tiêu đề. Xem ở đây.

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

    request.Accept = "application/json";

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

1

Về cơ bản, không. Đó là một tiêu đề http, vì vậy rất hợp lý khi sử dụng HttpWebRequestvà đặt .Referer(như bạn chỉ ra trong câu hỏi):

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

1

Lưu ý: giải pháp này sẽ hoạt động với WebClientSocket cũng như với HTTPWebRequest hoặc bất kỳ lớp nào khác sử dụng WebHeaderCollection để làm việc với các tiêu đề.

Nếu bạn nhìn vào mã nguồn của WebHeaderCollection.cs bạn sẽ thấy Hinfo được sử dụng để giữ thông tin của tất cả các tiêu đề đã biết:

private static readonly HeaderInfoTable HInfo = new HeaderInfoTable();

Nhìn vào lớp HeaderInfoTable, bạn có thể nhận thấy tất cả dữ liệu được lưu trữ trong bảng băm

private static Hashtable HeaderHashTable;

Hơn nữa, trong bộ điều khiển tĩnh của HeaderInfoTable, bạn có thể thấy tất cả các tiêu đề đã biết được thêm vào trong mảng HeaderInfo và sau đó được sao chép vào hashtable.

Nhìn cuối cùng vào lớp HeaderInfo hiển thị tên của các trường.

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

Vì vậy, với tất cả những điều trên, đây là một mã sử dụng sự phản chiếu để tìm Hashtable tĩnh trong lớp HeaderInfoTable và sau đó thay đổi mọi HeaderInfo bị giới hạn yêu cầu trong bảng băm không bị hạn chế

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

                        }
                    }

                }
            }
        } 

Xuất sắc! Điều này cũng cho phép đặt các tiêu đề đó cho yêu cầu được sử dụng khi thiết lập các ổ cắm web và do đó giải quyết vấn đề này: github.com/dotnet/corefx/issues/26627
ystein Kolsrud

Đó là trường hợp bởi vì tất cả đều sử dụng WebHeaderCollection để thao tác các tiêu đề. Tôi đã thử nghiệm nó trên httpWebRequest chỉ tho.
Ngủ

0

Tôi đang sử dụng chỉ:

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

0

Bạn chỉ có thể truyền WebRequest thành một httpWebRequest hiển thị bên dưới:

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

và sau đó thay vì cố gắng thao túng danh sách tiêu đề, hãy áp dụng nó trực tiếp trong yêu cầu thuộc tính request.Referer:

request.Referer = "yourReferer";

Các thuộc tính này có sẵn trong đối tượng yêu cầu.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.