use*_*441 7 c# httpclient httpwebrequest http-headers
尽管 RFC 声明唯一命名的标头的顺序无关紧要,但我发送此请求的网站确实对标头的顺序进行了检查。
这有效:
GET https://www.thewebsite.com HTTP/1.1
Host: www.thewebsite.com
Connection: keep-alive
Accept: */*
User-Agent: Mozilla/5.0 etc
Run Code Online (Sandbox Code Playgroud)
这不起作用:
GET https://www.thewebsite.com HTTP/1.1
Accept: */*
User-Agent: Mozilla/5.0 etc
Host: www.thewebsite.com
Connection: keep-alive
Run Code Online (Sandbox Code Playgroud)
默认情况下HttpWebRequest似乎将Host和Connection标题放在最后,在空行之前,而不是在 url 之后。
有什么方法(HttpWebRequest甚至在 Nuget 中使用 fork或其他库)来指定HttpWebRequest.
如果可能的话,我宁愿不开始沿着实现代理的路线对它们进行排序或不得不使用TcpClient.
我很感激这方面的任何提示。
更新:随着 Fiddler 的运行,HttpWebrequest 中的标头顺序可以在 CustomRules.cs 中重新排列。尽管如此,仍然没有更接近没有代理的解决方案。
一些服务器实现标头排序作为对任何攻击或垃圾邮件的预防措施,一篇文章解释了为什么排序 HTTP 标头很重要。
HttpWebRequest,没有简单的方法来对标头进行排序,并且 和Connection是Host在内部添加的。
如果排序确实很重要,请使用 the代替,它可以根据@Jason 的示例HttpClient轻松排列。Headers
如果您将使用HttpClient,您可以创建一个自定义HttpClientHandler,然后可以从那里安排您的标题。它可以是这样的。
处理程序
public class CustomHttpClientHandler : HttpClientHandler
{
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
request.Headers.Clear();
request.Headers.Add("Host", $"{request.RequestUri.Authority}");
request.Headers.Add("Connection", "keep-alive");
request.Headers.Add("Accept", "*/*");
request.Headers.Add("User-Agent", "Mozilla/5.0 etc");
return await base.SendAsync(request, cancellationToken);
}
}
Run Code Online (Sandbox Code Playgroud)
执行
HttpClient clientRequest = new HttpClient(new CustomHttpClientHandler());
await clientRequest.GetAsync(url);
Run Code Online (Sandbox Code Playgroud)
如果您自己设置标题,则可以指定顺序。添加公共标头时,它将查找现有标头而不是附加它们:
using System.Net;
namespace ConsoleApp2
{
class Program
{
static void Main(string[] args)
{
var request = WebRequest.Create("http://www.google.com");
request.Headers.Add("Host", "www.google.com");
// this will be set within GetResponse.
request.Headers.Add("Connection", "");
request.Headers.Add("Accept", "*/*");
request.Headers.Add("User-Agent", "Mozilla/5.0 etc");
request.GetResponse();
}
}
}
Run Code Online (Sandbox Code Playgroud)
这是一个示例HttpClient:
using System.Net.Http;
using System.Threading.Tasks;
namespace ConsoleApp3
{
class Program
{
static async Task Main(string[] args)
{
var client = new HttpClient();
client.DefaultRequestHeaders.Add("Host", "www.google.com");
client.DefaultRequestHeaders.Add("Connection", "keep-alive");
client.DefaultRequestHeaders.Add("Accept", "*/*");
client.DefaultRequestHeaders.Add("User-Agent", "Mozilla/5.0 etc");
await client.GetAsync("http://www.google.com");
await client.PostAsync("http://www.google.com", new StringContent(""));
}
}
}
Run Code Online (Sandbox Code Playgroud)
编辑 以上代码仅适用于.Net Framework .Net Core
在 .Net Framework 上,标头是保留的,因此不能像这样设置它们,请参阅使用 System.Net.WebRequest 时无法设置某些 HTTP 标头。
一种解决方法是使用反射来修改框架类的行为,但请注意,如果更新库,这可能会中断,因此不建议这样做!。
本质上,HttpWebRequest调用ToString序列WebHeaderCollection化。请参阅https://referencesource.microsoft.com/#System/net/System/Net/HttpWebRequest.cs,5079
因此可以创建一个自定义类来覆盖ToString. 不幸的是,需要反射来将标头设置为将WebRequest分配时的集合复制到Headers,而不是采用新的引用。
警告,如果框架更改,以下代码可能会中断
如果您使用此功能,请编写一些单元测试来验证更新到 .NET Framework 后行为仍然保持一致
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Reflection;
namespace ConsoleApp2
{
class Program
{
static void Main(string[] args)
{
// WARNING, CODE CAN BREAK IF FRAMEWORK CHANGES
// If you use this, write some unit tests that verify the behavior still stays consistent after updates to .NET Framework
var request = (HttpWebRequest)WebRequest.Create("http://www.google.com");
var field = typeof(HttpWebRequest).GetField("_HttpRequestHeaders", BindingFlags.Instance | BindingFlags.NonPublic);
var headers = new CustomWebHeaderCollection(new Dictionary<string, string>
{
["Host"] = "www.google.com",
["Connection"] = "keep-alive",
["Accept"] = "*/*",
["User-Agent"] = "Mozilla/5.0 etc"
});
field.SetValue(request, headers);
request.GetResponse();
}
}
internal class CustomWebHeaderCollection : WebHeaderCollection
{
private readonly Dictionary<string, string> _customHeaders;
public CustomWebHeaderCollection(Dictionary<string, string> customHeaders)
{
_customHeaders = customHeaders;
}
public override string ToString()
{
// Could call base.ToString() split on Newline and sort as needed
var lines = _customHeaders
.Select(kvp => $"{kvp.Key}: {kvp.Value}")
// These two new lines are needed after the HTTP header
.Concat(new [] { string.Empty, string.Empty });
var headers = string.Join("\r\n", lines);
return headers;
}
}
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
573 次 |
| 最近记录: |