使用 System.Net.WebRequest 时无法设置某些 HTTP 标头
本文关键字:设置 HTTP 标头 System Net WebRequest 使用 | 更新日期: 2023-09-27 17:47:23
当我尝试在WebRequest
对象上添加HTTP标头键/值对时,出现以下异常:
必须使用适当的属性修改此标头
我尝试使用 Add(( 方法向 Headers
集合添加新值,但仍然收到相同的异常。
webRequest.Headers.Add(HttpRequestHeader.Referer, "http://stackoverflow.com");
我可以通过将WebRequest对象转换为HttpWebRequest并设置httpWebReq.Referer ="http://stackoverflow.com"
等属性来解决此问题,但这仅适用于通过属性公开的少数标头。
我想知道是否有办法通过请求远程资源来更精细地控制修改标头。
如果您需要简短的技术性答案,请直接转到答案的最后一部分。
如果您想更好地了解,请阅读所有内容,我希望您会喜欢...
我今天也反驳了这个问题,今天我发现的是:
-
以上答案是正确的,因为:
1.1 它告诉您尝试添加的标头已经存在,然后您应该使用适当的属性(例如索引器(修改其值,而不是尝试再次添加它。
1.2 每当更改
HttpWebRequest
的标头时,都需要在对象本身上使用适当的属性(如果存在(。
感谢 FOR 和 Jvenema 的领先指南...
-
但是,我发现,这是拼图中缺失的部分:
2.1
WebHeaderCollection
类一般通过WebRequest
访问。标头或WebResponse
。头。某些常见标头被视为受限标头,由 API 直接公开(例如内容类型(或受系统保护,无法更改。
受限制的标头包括:
-
Accept
-
Connection
-
Content-Length
-
Content-Type
-
Date
-
Expect
-
Host
-
If-Modified-Since
-
Range
-
Referer
-
Transfer-Encoding
-
User-Agent
-
Proxy-Connection
因此,下次您遇到此异常并且不知道如何解决此问题时,请记住有一些受限制的标头,解决方案是使用 WebRequest
/HttpWebRequest
类中的相应属性显式修改它们的值。
编辑:(有用,来自评论,用户Kaido的评论(
解决方案是在调用 add 之前检查标头是否已经存在或受到限制 (
WebHeaderCollection.IsRestricted(key)
(
我在自定义 Web 客户端上遇到了这个问题。我认为人们可能会因为多种方法而感到困惑。 使用 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();
前面的所有答案都描述了问题,但没有提供解决方案。这是一个扩展方法,它通过允许您通过其字符串名称设置任何标头来解决问题。
用法
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
当我的代码尝试像这样设置"接受"标头值时,我遇到了同样的异常:
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";
每当更改HttpWebRequest
的标头时,都需要对对象本身使用适当的属性(如果存在(。如果你有一个普通的WebRequest
,一定要先把它投射到HttpWebRequest
。然后,可以通过 ((HttpWebRequest)request).Referrer
访问Referrer
,因此您无需直接修改标头 - 只需将属性设置为正确的值即可。 ContentLength
、ContentType
、UserAgent
等都需要这样设置。
恕我直言,这是 MS 部分的缺点......通过Headers.Add()
设置标头应该在后台自动调用相应的属性,如果这是他们想要做的。
WebRequest 是抽象的(并且由于任何继承类都必须覆盖 Headers 属性(.. 您使用的是哪个具体的 WebRequest?换句话说,你如何让这个 WebRequest 对象变得有 ?
她.. mnour的回答让我意识到你收到的错误消息实际上是正确的:它告诉你你尝试添加的标头已经存在,然后你应该使用适当的属性(例如索引器(修改其值,而不是尝试再次添加它。这可能就是你要找的。
从 WebRequest 继承的其他类可能具有更好的属性来包装某些标头;例如,请参阅此帖子。
注意:此解决方案将适用于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 类中查找静态哈希表,然后将哈希表内每个受请求限制的 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");
}
}
}
}
}
}
上面的答案都很好,但问题的本质是一些标头是以一种方式设置的,而另一些是另一种方式设置的。 有关"受限标头"列表,请参阅上文。 对于这些,您只需将它们设置为属性即可。 对于其他人,您实际上添加了标头。 看这里。
request.ContentType = "application/x-www-form-urlencoded";
request.Accept = "application/json";
request.Headers.Add(HttpRequestHeader.Authorization, "Basic " + info.clientId + ":" + info.clientSecret);
基本上没有。这是一个 http 标头,因此强制转换为 HttpWebRequest
并设置.Referer
是合理的(如您在问题中指出的那样(:
HttpWebRequest req = ...
req.Referer = "your url";
我只使用:
request.ContentType = "application/json; charset=utf-8"
您可以将 WebRequest 转换为如下所示的 HttpWebRequest:
var request = (HttpWebRequest)WebRequest.Create(myUri);
然后,不要尝试操作标头列表,而是直接在请求属性请求中应用它。推荐人:
request.Referer = "yourReferer";
这些属性在请求对象中可用。
我在下面遇到了同样的问题,这段代码对我有用
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
request.Headers["UserAgent"] = "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1;
Trident/5.0)"
request.Headers.UserAgent.Add(new ProductInfoHeaderValue("my_string"));