C# 使用 System.Net.WebRequest 时无法设置某些 HTTP 标头

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/239725/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-03 19:23:05  来源:igfitidea点击:

Cannot set some HTTP headers when using System.Net.WebRequest

c#headerwebrequest

提问by Vince Panuccio

When I try to add a HTTP header key/value pair on a WebRequestobject, I get the following exception:

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

This header must be modified using the appropriate property

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

I've tried adding new values to the Headerscollection by using the Add() method but I still get the same exception.

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

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

I can get around this by casting the WebRequest object to a HttpWebRequest and setting the properties such as httpWebReq.Referer ="http://stackoverflow.com", but this only works for a handful of headers that are exposed via properties.

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

I'd like to know if there's a way to get a finer grained control over modifying headers with a request for a remote resource.

我想知道是否有一种方法可以通过对远程资源的请求对修改标头进行更细粒度的控制。

采纳答案by dubi

If you need the short and technical answer go right to the last section of the answer.

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

If you want to know better, read it all, and i hope you'll enjoy...

如果您想更好地了解,请阅读所有内容,我希望您会喜欢...



I countered this problem too today, and what i discovered today is that:

我今天也解决了这个问题,我今天发现的是:

  1. the above answers are true, as:

    1.1 it's telling you that the header you are trying to add already exist and you should then modify its value using the appropriate property (the indexer, for instance), instead of trying to add it again.

    1.2 Anytime you're changing the headers of an HttpWebRequest, you need to use the appropriate properties on the object itself, if they exist.

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

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

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

Thanks FOR and Jvenema for the leading guidelines...

感谢 FOR 和 Jvenema 的主要指导方针...

  1. But, What i found out, and that was the missing piece in the puzzleis that:

    2.1 The WebHeaderCollectionclass is generally accessed through WebRequest.Headers or WebResponse.Headers. Some common headers are considered restricted and are either exposed directly by the API (such as Content-Type) or protected by the system and cannot be changed.

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

    2.1WebHeaderCollection类一般通过WebRequest.Headers或WebResponse.Headers访问。一些常见的标头被认为是受限的,要么直接由 API 公开(例如 Content-Type),要么受系统保护,无法更改。

The restricted headers are:

受限制的标头是:

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


So, next time you are facing this exception and don't know how to solve this, remember that there are some restricted headers, and the solution is to modify their values using the appropriate property explicitly from the WebRequest/HttpWebRequestclass.

因此,下次您遇到此异常并且不知道如何解决此问题时,请记住有一些受限制的标头,解决方案是使用WebRequest/HttpWebRequest类中的适当属性显式修改它们的值。



Edit: (useful, from comments, comment by user Kaido)

编辑:(有用,来自评论,用户Kaido评论)

Solution is to check if the header exists already or is restricted (WebHeaderCollection.IsRestricted(key)) before calling add

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

回答by FOR

WebRequestbeing abstract (and since any inheriting class must override the Headers property).. which concrete WebRequest are you using ? In other words, how do you get that WebRequest object to beign with ?

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

ehr.. mnour answer made me realize that the error message you were getting is actually spot on: it's telling you that the header you are trying to add already exist and you should then modify its value using the appropriate property (the indexer, for instance), instead of trying to add it again. That's probably all you were looking for.

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

Other classes inheriting from WebRequest might have even better properties wrapping certain headers; See this postfor instance.

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

回答by Marc Gravell

Basically, no. That is an http header, so it is reasonable to cast to HttpWebRequestand set the .Referer(as you indicate in the question):

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

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

回答by jvenema

Anytime you're changing the headers of an HttpWebRequest, you need to use the appropriate properties on the object itself, if they exist. If you have a plain WebRequest, be sure to cast it to an HttpWebRequestfirst. Then Referrerin your case can be accessed via ((HttpWebRequest)request).Referrer, so you don't need to modify the header directly - just set the property to the right value. ContentLength, ContentType, UserAgent, etc, all need to be set this way.

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

IMHO, this is a shortcoming on MS part...setting the headers via Headers.Add()should automatically call the appropriate property behind the scenes, if that's what they want to do.

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

回答by Chmod

I ran into this problem with a custom web client. I think people may be getting confused because of multiple ways to do this. When using WebRequest.Create()you can cast to an HttpWebRequestand use the property to add or modify a header. When using a WebHeaderCollectionyou may use the .Add("referer","my_url").

我在使用自定义 Web 客户端时遇到了这个问题。我认为人们可能会因为有多种方法而感到困惑。使用时,WebRequest.Create()您可以转换为 anHttpWebRequest并使用该属性添加或修改标题。使用 a 时,WebHeaderCollection您可以使用.Add("referer","my_url").

Ex 1

例 1

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

Ex 2

例 2

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

回答by Despertar

All the previous answers describe the problem without providing a solution. Here is an extension method which solves the problem by allowing you to set any header via its string name.

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

Usage

用法

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

Extension Class

扩展类

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

Scenarios

场景

I wrote a wrapper for HttpWebRequestand didn't want to expose all 13 restricted headers as properties in my wrapper. Instead I wanted to use a simple Dictionary<string, string>.

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

Another example is an HTTP proxy where you need to take headers in a request and forward them to the recipient.

另一个示例是 HTTP 代理,您需要在请求中获取标头并将其转发给接收方。

There are a lot of other scenarios where its just not practical or possible to use properties. Forcing the user to set the header via a property is a very inflexible design which is why reflection is needed. The up-side is that the reflection is abstracted away, it's still fast (.001 second in my tests), and as an extension method feels natural.

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

Notes

笔记

Header names are case insensitive per the RFC, http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2

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

回答by Rob

The above answers are all fine, but the essence of the issue is that some headers are set one way, and others are set other ways. See above for 'restricted header' lists. FOr these, you just set them as a property. For others, you actually add the header. See here.

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

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

    request.Accept = "application/json";

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

回答by Stefan Michev

I'm using just:

我只使用:

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

回答by Bonomi

You can just cast the WebRequest to an HttpWebRequest showed below:

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

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

and then instead of trying to manipulate the header list, apply it directly in the request property request.Referer:

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

request.Referer = "yourReferer";

These properties are available in the request object.

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

回答by Mike Gledhill

I had the same exception when my code tried to set the "Accept" header value like this:

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

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

The solution was to change it to this:

解决方案是将其更改为:

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