在.Net中使用HttpClient与HTTP 2进行通信。

21

我正在尝试通过HTTP 2.0请求数据。我正在使用来自.Net Core 2.2的HttpClient。我正在使用Windows 10,但将在生产中运行Linux。问题是响应中的版本似乎始终为“1.1”。我做错了什么?

using (var client = new HttpClient())
{
    using (var request = new HttpRequestMessage(new HttpMethod("GET"),
    "https://duckduckgo.com/"
    ))
    {
        request.Version = new Version(2, 0);
        var response = await client.SendAsync(request);

        Console.WriteLine(response.Version);
    }
}
3个回答

25

更新- .NET Core 3.0

.NET Core 3.0现在支持HTTP/2。以下代码将打印2.0

var client = new HttpClient();

var req = new HttpRequestMessage(HttpMethod.Get, "https://http2.akamai.com/demo"){
             Version = new Version(2, 0) 
             };

var x = await client.SendAsync(req);
var version = x.Version;

Console.WriteLine(version);

原始回答

即使你在请求中明确设置了版本号,也无法在.NET Core 2.1或2.2中使用HttpClient的HTTP/2。你需要显式配置.NET Core以使用随.NET Core 2.0一起提供的旧HttpClientHandler实例,可以通过设置App Context开关来实现:

AppContext.SetSwitch("System.Net.Http.UseSocketsHttpHandler", false);

或者通过将DOTNET_SYSTEM_NET_HTTP_USESOCKETSHTTPHANDLER环境变量设置为0false

这个Github问题中的讨论显示,.NET Core 3.0计划支持HTTP/2。在Microsoft Connect 2018发布的3.0 Preview 1尚不支持HTTP/2。

HttpClientHandler在.NET Core 2.0之前使用支持HTTP/2。以下代码将在针对Core 2.0的项目中返回2.0

var client = new HttpClient();

var req = new HttpRequestMessage(HttpMethod.Get, "https://http2.akamai.com/demo")
{
    Version = new Version(2, 0)
};

var x = await client.SendAsync(req);
var version = x.Version;

Console.WriteLine(version);

如果更改目标运行时,请确保彻底清理项目-删除binobj和所有目标文件,否则可能会像我一样使用错误的运行时。

在2.1中,默认添加了一个新的、速度更快的SocketsHttpClientHandler。这个新的处理程序尚不支持HTTP/2。相同的代码将返回协议版本1.1

但是,如果在创建HttpClient之前设置应用程序上下文切换,则会使用HTTP/2。此代码将返回协议版本2.0。有趣的是,无需指定HTTP版本。当HTTP/2可用时,实际协议版本是经过协商的。即使未指定版本,Akamai URL和https://www.google.com都将使用HTTP/2:

AppContext.SetSwitch("System.Net.Http.UseSocketsHttpHandler", false);
var client = new HttpClient();

var req = new HttpRequestMessage(HttpMethod.Get, "https://http2.akamai.com/demo");
var x = await client.SendAsync(req);

var version = x.Version;

.NET Core 2.1 SDK 预览版2的官方公告中解释了开关和环境变量:

Sockets Performance and SocketsHttpHandler

We made major improvements to sockets in .NET Core 2.1. Sockets are the basis of both outgoing and incoming networking communication. The higher-level networking APIs in .NET Core 2.1, including HttpClient and Kestrel, are now based on .NET sockets. In earlier versions, these higher-level APIs were based on native networking implementations.

...

You can use one of the following mechanisms to configure a process to use the older HttpClientHandler:

From code, use the AppContext class:

AppContext.SetSwitch("System.Net.Http.UseSocketsHttpHandler", false);

The AppContext switch can also be set by config file.

The same can be achieved via the environment variable DOTNET_SYSTEM_NET_HTTP_USESOCKETSHTTPHANDLER. To opt out, set the value to either false or 0.


谢谢你的回答。根据你说的,我已经让它在Windows 10上运行了!你知道是否有办法让它在Ubuntu上运行吗?我遇到了这个错误:System.TypeInitializationException: The type initializer for 'System.Net.Http.CurlHandler' threw an exception. --> System.TypeInitializationException: The type initializer for 'Http' threw an exception. --> System.TypeInitializationException: The type initializer for 'HttpInitializer' threw an exception. --> System.DllNotFoundException: Unable to load shared library 'System.Net.Http.Native' or one of its dependencies - Robert M
根据@RobertM的公告和错误说明,如果您想使用旧版HttpClientHandler,则需要安装旧版先决条件,如libcurl和openssl。旧版HttpClientHandler在每个操作系统上使用不同的库。一个快速而简单的解决方案可能是首先安装2.0版本。 - Panagiotis Kanavos
非常感谢!确实,采用2.0版本应该是最少头痛的方法,直到3.0版本发布。 - Robert M
如果您不需要HTTP/1兼容性/回退并且不在意一些缺少的功能,您可以尝试我的C#本地HTTP/2实现,该实现应该可以在.NET Core 2.0上工作:https://github.com/Matthias247/http2dotnet - Matthias247

5

目前建议的模式是在ConfigureServices方法中注册HttpClient依赖项: https://learn.microsoft.com/en-us/aspnet/core/fundamentals/http-requests.

以下是使用类型化客户端注册默认HttpClient并配置其使用HTTP/2的示例:

public void ConfigureServices(IServiceCollection services)
{
...
    services.AddHttpClient<ExampleService>()
        .ConfigureHttpClient((client) =>
        {
            client.DefaultRequestVersion = new Version(2, 0);
        });
...
}

4
适用于所有 HttpClient 方法,除了 SendAsync。https://learn.microsoft.com/en-us/dotnet/api/system.net.http.httpclient.defaultrequestversion?view=net-6.0#remarks - Gunnar Már Óttarsson
@GunnarMárÓttarsson 非常感谢,我没有注意到这个细节! - vusaldev

1
您希望使用http/2协议,但未设置协议属性。请设置该属性。您是否检查了您所使用的操作系统是否支持?例如,.NET Core尚不支持Mac OS。此外,请确保按照此处描述的方式设置所需的属性,使用选项变量调用ConfigureKestrel时。从逻辑上讲,您也可以尝试这种替代方法来设置http版本。
通过进行curl调用,您可以检查您的http/2调用在您的情况下是否可行。
此外,在.NET Core中存在与原始HttpClient相关的问题(您正在使用它)。请使用HttpClientFactory

微软的文档提到了“原始和著名的HttpClient类”。我的意思完全一样,我总是尽量与微软的文档保持一致,这样人们就知道我在说什么。你说得有道理,因为实际上并没有新的类,只是用你提到的新处理程序的新方法创建了一个新的类。https://learn.microsoft.com/en-us/dotnet/standard/microservices-architecture/implement-resilient-applications/use-httpclientfactory-to-implement-resilient-http-requests - Daan
该文档是针对.NET 4.x HttpClient而非.NET Core的。HttpClientFactory创建的是.NET Core HttpClient实例。在.NET Core中,您不能使用旧的HttpClient类。 - Panagiotis Kanavos
“原始且广为人知的HttpClient类”是确切的引用。 - Daan
感谢您花费时间回答我的问题,但它仍然输出“1.1”。我不确定如何设置HttpRequest.Protocol属性,您确定这与HttpClient有关吗?我已经检查了curl和浏览器是否支持http/2,并且对于“https://duckduckgo.com/”是支持的。您关于Kestrel选项的说法是正确的,但正如您所看到的,我正在向Web上的现有服务器(DuckDuckGo)发送请求。我使用的是最新的Windows 10版本。我已经在HttpRequestMessage请求消息中附加了版本信息。 - Robert M
1
@PanagiotisKanavos,为什么不呢?.Net Core 2.2缺少什么来支持HTTP/2? - Kok How Teh
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接