136

当我尝试在对象上添加 HTTP 标头键/值对时WebRequest,出现以下异常:

必须使用适当的属性修改此标头

我尝试Headers使用 Add() 方法将新值添加到集合中,但仍然遇到相同的异常。

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

我可以通过将 WebRequest 对象转换为 HttpWebRequest 并设置诸如 之类的属性来解决此问题httpWebReq.Referer ="http://stackoverflow.com",但这仅适用于通过属性公开的少数标头。

我想知道是否有办法通过请求远程资源来更精细地控制修改标头。

4

12 回答 12

184

如果您需要简短的技术性答案,请直接阅读答案的最后一部分。

如果您想了解更多,请阅读全部内容,希望您会喜欢...


我今天也反驳了这个问题,我今天发现的是:

  1. 上面的答案都是正确的,因为:

    1.1 它告诉您您尝试添加的标头已经存在,然后您应该使用适当的属性(例如索引器)修改其值,而不是尝试再次添加它。

    1.2 每当您更改 的标题时HttpWebRequest,您需要在对象本身上使用适当的属性(如果存在)。

感谢 FOR 和 Jvenema 的领先指导方针......

  1. 但是,我发现,这是拼图中缺失的部分

    2.1WebHeaderCollection类一般通过WebRequest.Headers或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)解决方案是在调用 add 之前检查标头是否已经存在或被限制( )

于 2011-01-20T20:44:03.300 回答
80

我在使用自定义 Web 客户端时遇到了这个问题。我认为人们可能会因为有多种方法而感到困惑。使用时,WebRequest.Create()您可以强制转换为HttpWebRequest并使用该属性来添加或修改标题。使用 a 时,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();
于 2010-03-08T23:47:55.933 回答
33

之前的所有答案都描述了问题而没有提供解决方案。这是一种扩展方法,通过允许您通过其字符串名称设置任何标题来解决问题。

用法

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 代理,您需要在请求中获取标头并将它们转发给接收者。

在许多其他场景中,它只是不切实际或无法使用属性。强制用户通过属性设置标题是一种非常不灵活的设计,这就是需要反射的原因。好处是反射被抽象掉了,它仍然很快(在我的测试中是 0.001 秒),并且作为一种扩展方法感觉很自然。

笔记

根据 RFC,标头名称不区分大小写,http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2

于 2014-04-14T21:37:42.417 回答
13

当我的代码尝试像这样设置“Accept”标头值时,我遇到了同样的异常:

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";
于 2016-03-16T14:27:40.243 回答
12

每当您更改 的标头时HttpWebRequest,都需要在对象本身上使用适当的属性(如果存在)。如果您有一个 plain WebRequest,请务必将其转换为HttpWebRequest第一个。然后Referrer在您的情况下可以通过 访问((HttpWebRequest)request).Referrer,因此您无需直接修改标题 - 只需将属性设置为正确的值即可。ContentLength, ContentType,UserAgent等都需要这样设置。

恕我直言,这是 MS 部分的一个缺点......通过设置标题Headers.Add()应该自动在幕后调用适当的属性,如果这是他们想要做的。

于 2008-12-01T16:20:18.530 回答
7

WebRequest是抽象的(并且由于任何继承类都必须覆盖 Headers 属性)..您使用的是哪个具体的 WebRequest ?换句话说,你如何让那个 WebRequest 对象使用?

ehr .. mnour 的回答让我意识到您收到的错误消息实际上是正确的:它告诉您您尝试添加的标头已经存在,然后您应该使用适当的属性(例如索引器)修改它的值),而不是尝试再次添加它。这可能就是你要找的所有东西。

从 WebRequest 继承的其他类可能具有更好的属性来包装某些标头;例如看这篇文章

于 2008-10-27T12:37:59.763 回答
2

上面的答案都很好,但问题的本质是一些标题设置为一种方式,而另一些则设置为另一种方式。有关“受限标题”列表,请参见上文。对于这些,您只需将它们设置为属性。对于其他人,您实际上添加了标题。看这里。

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

    request.Accept = "application/json";

    request.Headers.Add(HttpRequestHeader.Authorization, "Basic " + info.clientId + ":" + info.clientSecret);
于 2014-12-30T17:35:03.833 回答
1

基本上,没有。那是一个http标头,因此可以合理地转换为HttpWebRequest并设置.Referer(如您在问题中指出的那样):

HttpWebRequest req = ...
req.Referer = "your url";
于 2008-10-27T13:19:27.950 回答
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;
    ...
    }

因此,综上所述,这里有一个代码,它使用反射在 HeaderInfoTable 类中查找静态 Hashtable,然后将哈希表中每个请求限制的 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");
                            }

                        }
                    }

                }
            }
        } 
于 2019-10-28T04:34:45.653 回答
0

我只使用:

request.ContentType = "application/json; charset=utf-8"
于 2015-03-11T17:16:47.747 回答
0

您可以将 WebRequest 转换为 HttpWebRequest,如下所示:

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

然后不是试图操纵标头列表,而是直接在请求属性request.Referer中应用它:

request.Referer = "yourReferer";

这些属性在请求对象中可用。

于 2015-04-24T14:48:42.160 回答
0

我在为我工作的一段代码下面遇到了同样的问题

HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);

request.Headers["UserAgent"] = "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; 
Trident/5.0)"
于 2020-11-25T08:19:56.957 回答