【问题标题】:unable to make httpclientfactory working from a .net framework 4.7.2 winform project无法使 httpclientfactory 从 .net 框架 4.7.2 winform 项目工作
【发布时间】:2022-10-07 19:49:47
【问题描述】:

我阅读了我能找到的关于HttpClientFactory 的所有内容,经过几天的努力,我即将放弃,但在这里抓住机会以防有人能提供帮助。我只是想在我的 .net 4.7.2 Winforms 应用程序中为 rest-api 客户端实现HttpClientFactory

我尝试实现 Typed 和 Named 客户端,但是每次我尝试在我的代码中实例化它时都会得到一个空引用。所以这里我到目前为止做了什么:

对于 Typed Client,我创建了一个类:

Imports System.Net.Http
Imports Microsoft.Extensions.Http
Imports Microsoft.Extensions.DependencyInjection

Public Class TypedCustomHTTPClient

    Public Property _Client() As HttpClient
    Public Sub New(ByVal httpClient As HttpClient)
        'httpClient.BaseAddress = New Uri("https://api.google.com/")
        httpClient.DefaultRequestHeaders.Add("Accept", "application/json")
        httpClient.DefaultRequestHeaders.Add("User-Agent", "HttpClientFactory-Sample")
        _Client = httpClient

    End Sub


End Class

然后我的 Project Main Sub 我正在注册我的 Typed Client 也是一个 Named Client(我不确定我是否以正确的方式进行操作)

Imports Microsoft.Extensions.DependencyInjection
Imports Microsoft.Extensions.Hosting
Imports Microsoft.Extensions.Http
Imports Polly


Module MainModule


    Public Sub Main()


        Dim seviceColl As New ServiceCollection

        '
        
        '--------Registering and injecting HttpClients (by Name)
        seviceColl.AddHttpClient("s2sfHTTPClient", Sub(c)
                                                            'c.BaseAddress = New Uri("https://api.google.com/")
                                                            c.DefaultRequestHeaders.Add("Accept", "application/json")
                                                        End Sub).AddPolicyHandler(PolHolder.httpRetryWithReauthorizationPolicy())
        seviceColl.AddHttpClient("GitHub", Sub(httpClient)
                                                    httpClient.BaseAddress = New Uri("https://api.github.com/")

                                                    ' using Microsoft.Net.Http.Headers;
                                                    ' The GitHub API requires two headers.
                                                    ''httpClient.DefaultRequestHeaders.Add(HeaderNames.Accept, "application/vnd.github.v3+json")
                                                    ''httpClient.DefaultRequestHeaders.Add(HeaderNames.UserAgent, "HttpRequestsSample")
                                                End Sub)


       'Registering and injecting HttpClients (by Type)

        seviceColl.AddHttpClient(Of TypedCustomHTTPClient)().SetHandlerLifetime(TimeSpan.FromMinutes(5)).AddPolicyHandler(PolHolder.httpRetryWithReauthorizationPolicy()) 'Set lifetime to five minutes
       
        'Building Service Provider
        Dim serviceprovider = servicecoll.BuildServiceProvider
        '
        '
        '
        Application.Run(New Form1()) ''//Use your main form here
    End Sub

End Module

当我尝试使用类型化客户端或命名客户端(应使用.CreateClient 方法)时,我在CreateClient 行上收到空引用错误。

  Private Property _httpClientFactory As IHttpClientFactory
Public Function TestQuery2(ByVal soqlQuery As String) As String
        Dim customclient = _httpClientFactory.CreateClient("s2sfHTTPClient")
        'Using customclient._Client '= New HttpClient()
        '
        Dim restRequest As String = InstanceUrl + API_ENDPOINT & "query/?q=" & soqlQuery
            Dim request = New HttpRequestMessage(HttpMethod.[Get], restRequest)
            request.Headers.Add("Authorization", "Bearer " & AuthToken)
            request.Headers.Accept.Add(New MediaTypeWithQualityHeaderValue("application/json"))
            request.Headers.Add("X-PrettyPrint", "1")
        Dim response = customclient.SendAsync(request).Result
        Return response.Content.ReadAsStringAsync().Result
        'End Using
    End Function

任何想法?我究竟做错了什么?

【问题讨论】:

  • 如果您在 CreateClient 行收到 NullReferenceException,这意味着 IHttpClientFactory 为空。
  • 根据共享代码,我无法发现您在 ServiceCollection 上调用 BuildServiceProvider 的位置。
  • @PeterCsala 感谢您的回复。我在发布后不久就发现了这一点,但即使我将 .BuildServiceProvider 行添加到我的 Main Sub(请参阅我的问题中的更新代码),我似乎无法从我的其他任何地方获得对我正在创建的 HttpclientFactory 的引用代码。这是为什么?我什至尝试使用 SimpleInjector 容器,但仍然没有运气。我如何获得对我从主子创建的 httpclientfactory 的引用?在网上找到的所有关于这个的文档都说上面应该可以工作,但是找到的所有信息主要是指.net核心,而不是完整的框架

标签: vb.net dotnet-httpclient polly .net-4.7.2 ihttpclientfactory


【解决方案1】:

为了能够在 .NET Framework 控制台应用程序中使用 IHttpClientFactory,您需要以下内容:

(我很长时间没有使用 Visual Basic,所以我分享了 C# 中的示例代码。)

using System;
using System.Net.Http;
using Microsoft.Extensions.DependencyInjection;

class Program
{
    private static readonly ServiceProvider serviceProvider;

    static Program()
    {
        serviceProvider = new ServiceCollection().AddHttpClient().BuildServiceProvider();
    }

    public static void Main(string[] args)
    {
        var factory = serviceProvider.GetService<IHttpClientFactory>();
        Console.WriteLine(factory == null); //False
    }
}
  • 我们创建一个ServiceCollection 并在其上调用AddHttpClient 扩展方法
    • 它将DefaultHttpClientFactory 注册为IHttpClientFactory 的单例
  • 我们从serviceCollection 构建一个serviceProvider
  • 我们从serviceProvider 中检索注册的IHttpClientFactory

如果您更喜欢使用SimpleInjector,则代码应如下所示:

using System;
using System.Net.Http;
using Microsoft.Extensions.DependencyInjection;
using SimpleInjector;

class Program
{
    private static readonly Container container = new Container();

    static Program()
    {
        var serviceProvider = new ServiceCollection().AddHttpClient().BuildServiceProvider();
        container.RegisterInstance(serviceProvider.GetService<IHttpClientFactory>());

        //These are not mandatory calls but highly recommended
        //container.ContainerScope.RegisterForDisposal(serviceProvider);
        //container.Verify();
    }

    public static void Main(string[] args)
    {
        var factory = container.GetInstance<IHttpClientFactory>();
        Console.WriteLine(factory == null); //False
    }
}
  • 这里我们从 MS DI 中检索已注册的 IHttpClientFactory 实现
  • 然后我们在SimpleInjector的容器中重新注册它

更新#1使用命名客户端

class Program
{
    private static readonly Container container = new Container();
    private const string ClientName = "namedClient";
    static Program()
    {
        var serviceCollection = new ServiceCollection().AddHttpClient();
        serviceCollection.AddHttpClient(ClientName, c => c.BaseAddress = new Uri("https://httpstat.us/"));
        var serviceProvider = serviceCollection.BuildServiceProvider();
        container.RegisterInstance(serviceProvider.GetService<IHttpClientFactory>());
    }

    public static void Main()
    {
        var factory = container.GetInstance<IHttpClientFactory>();
        var client = factory.CreateClient(ClientName);
        Console.WriteLine( client.BaseAddress); // https://httpstat.us/
    }
}

更新#2使用类型化客户端

class Program
{
    private static readonly Container container = new Container();
    private const string ClientName = "namedClient";

    static Program()
    {
        var serviceCollection = new ServiceCollection().AddHttpClient();
        serviceCollection.AddHttpClient(ClientName, c => c.BaseAddress = new Uri("https://httpstat.us/"));
        serviceCollection.AddHttpClient<ITestClient, TestClient>();
        var serviceProvider = serviceCollection.BuildServiceProvider();

        container.RegisterInstance(serviceProvider.GetService<IHttpClientFactory>());
        container.RegisterInstance(serviceProvider.GetService<ITypedHttpClientFactory<TestClient>>());
    }

    public static void Main(string[] args)
    {
        var namedFactory = container.GetInstance<IHttpClientFactory>();
        var typedFactory = container.GetInstance<ITypedHttpClientFactory<TestClient>>();
        var client = typedFactory.CreateClient(namedFactory.CreateClient(ClientName));
        Console.WriteLine(client == null); // False
    }
}

请注意ITypedHttpClientFactory的类型参数是具体类型(TestClient),而不是接口(@98​​7654340@)


更新#3

我只需要保留对容器的引用,以便稍后从代码中的任何位置检索工厂并调用工作正常的CreateClient 方法。

这个依赖注入包是为 ASP.NET Core 设计的(并且是它的一部分)。 .NET Framework 控制台/WinForms 应用程序无法真正发挥其真正的威力。

GetServiceGetRequiredService 调用都充当服务定位器而不是作为依赖注入器. ASP.NET Core 可以使用此库通过构造函数或属性注入依赖项。

我唯一不明白的是,为什么在您的代码中,对于 Typed 客户端示例,您还使用 NamedClient 及其 CreateClient 方法?

这是个好问题。 typedFactory.CreateClient 调用预期 HttpClient 实例。如果您要传递一个全新的实例

var client = typedFactory.CreateClient(new HttpClient());        

那么预配置的BaseAddress (, c =&gt; c.BaseAddress = new Uri("https://httpstat.us/"));) 将在那里不可用。

因此,如果您有一个预先配置的命名客户端,那么您可以检索它并使用它来创建一个类型化的客户端。 (最终结果将是一个命名的类型化客户端。)

【讨论】:

  • 我在某个地方看到过这个代码示例,也许是您在 Stack 上发表的另一篇文章,但它没有回答与我的场景有关的问题,即从 Main Subi 想要注册一个自定义类型和一个自定义命名 HttpClient(使用 AddHttpClient 方法),并且似乎无法从我的 MAIn 程序中获得对我正在构建的工厂的非空引用。您提供的代码完全符合您的描述(注册默认 HttpclientFactory),然后您使用 GetService 检索它,但不幸的是对我想做的事情没有帮助
  • @MartinT 我更新了我的帖子以包含两个示例来展示如何创建命名和类型化客户端。这些是否涵盖了您的用例?
  • 非常感谢你的代码。我最终只使用了命名客户端。我只需要保留对容器的引用,以便稍后从代码中的任何位置检索工厂,并调用可以正常工作的 Createclient 方法。我唯一不明白的是,为什么在您的代码中,对于 Typed 客户端示例,您还使用 NamedClient 及其 CreateClient 方法?在以下链接中,我曾经尝试学习所有这些 DI 和 httpclientfactory 概念,它说:“类型化的客户端可以直接注入和使用:”(比较他们的代码和你的代码让我感到困惑。
  • 这是我所指的链接:[链接] (learn.microsoft.com/en-us/aspnet/core/fundamentals/…)斜体 大胆的
  • @MartinT 我已经修改了我的帖子以反映您的问题。请检查一下!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-10-02
  • 2020-09-27
  • 1970-01-01
  • 1970-01-01
  • 2022-12-10
  • 2021-04-16
相关资源
最近更新 更多