使用System.Net.WebRequest时不能设置一些HTTP标头

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

这个头文件必须使用相应的属性进行修改

我已经尝试使用Add()方法向Headers集合中添加新的值,但我仍然得到相同的exception。

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

我可以通过将WebRequest对象强制转换为HttpWebRequest并设置诸如httpWebReq.Referer ="http://stackoverflow.com"的属性来解决这个问题,但是这只适用于通过属性暴露的less数头文件。

我想知道是否有办法获得一个更好的粒度控制修改标题与远程资源的请求。

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

如果你想知道更多,请阅读,我希望你会喜欢…


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

  1. 以上答案是真实的,如下所示:

    1.1它告诉你,你试图添加的头文件已经存在,你应该使用合适的属性(例如索引器)来修改它的值,而不是试图再次添加它。

    1.2只要你改变了HttpWebRequest的头部,你就需要在对象本身上使用合适的属性(如果它们存在的话)。

感谢FOR和Jvenema的领先准则…

  1. 但是,我发现了这个问题 ,那就是:

    2.1 WebHeaderCollection类通常通过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

因此,下一次遇到这个exception,不知道如何解决这个问题时,请记住有一些受限制的头文件,解决方法是使用WebRequest / HttpWebRequest类中显式的相应属性修改它们的值。


编辑:(有用的,从评论,用户评论Kaido )

解决scheme是在调用add之前检查头是否已经存在或者被限制( WebHeaderCollection.IsRestricted(key)

我遇到了一个自定义Web客户端的问题。 我认为人们可能会因为多种方式而感到困惑。 使用WebRequest.Create()您可以将其HttpWebRequest转换为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(); 

所有以前的答案都没有提供解决scheme来描述问题。 这里是一个扩展方法,它通过允许你通过string名来设置任何头来解决这个问题。

用法

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

scheme

我为HttpWebRequest编写了一个包装器,并不想将所有13个受限制的头部作为包装器中的属性公开。 相反,我想用一个简单的Dictionary<string, string>

另一个例子是一个HTTP代理,你需要在请求中获取头文件并将其转发给收件人。

还有很多其他情况下,它只是不实际或可能使用属性。 强制用户通过属性设置标题是一个非常不灵活的devise,这就是为什么需要reflection。 反面是reflection被抽象了,它仍然很快(在我的testing中0.001秒),并且作为延伸方法感觉自然。

笔记

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

每当你改变一个HttpWebRequest的头部时,你需要在对象本身上使用适当的属性(如果它们存在的话)。 如果你有一个普通的WebRequest ,一定要先把它转换成一个HttpWebRequest 。 然后在你的情况下,推荐人可以通过((HttpWebRequest)request).Referrer ,所以你不需要直接修改头 – 只要将属性设置为正确的值。 ContentLengthContentTypeUserAgent等,都需要这样设置。

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

WebRequest是抽象的(因为任何inheritance类必须重写Headers属性)..你使用了哪个具体的WebRequest? 换句话说,你如何获得WebRequest对象?

ehr .. mnour的答案让我意识到,你得到的错误信息实际上是在发现:它告诉你,你试图添加的头已经存在,你应该修改它的值,使用适当的属性(例如索引器),而不是试图再次添加它。 这可能是你正在寻找的一切。

从WebRequestinheritance的其他类可能有更好的包装某些标题的属性; 看到这个职位 ,例如。

当我的代码尝试设置“Accept”标题值时,我有同样的exception:

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

基本上没有。 这是一个http头,所以将其转换为HttpWebRequest并设置.Referer (如您在问题中指出的)是合理的:

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

上面的答案都很好,但问题的实质是一些标题设置为一种方式,其他标题设置为其他方式。 请参阅上面的“受限制标题”列表。 对于这些,你只需将它们设置为一个属性。 对于其他人,你实际上添加标题。 看这里。

  request.ContentType = "application/x-www-form-urlencoded"; request.Accept = "application/json"; request.Headers.Add(HttpRequestHeader.Authorization, "Basic " + info.clientId + ":" + info.clientSecret); 

我正在使用:

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

您可以将WebRequest强制转换为如下所示的HttpWebRequest:

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

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

 request.Referer = "yourReferer"; 

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