C# 单例httpclient与创建新的httpclient请求

C# 单例httpclient与创建新的httpclient请求,c#,xamarin,design-patterns,httpclient,C#,Xamarin,Design Patterns,Httpclient,我正在尝试在我的Xamarin.Formsmobile应用程序中使用HttpClient为webservice创建层 无singlton模式 单件模式 在第一种方法中,我在每个新请求中创建新的http客户机对象 通过移动应用程序 这是我的密码 public HttpClient GetConnection() { HttpClient httpClient = new HttpClient(); httpClient.Base

我正在尝试在我的
Xamarin.Forms
mobile应用程序中使用HttpClient为webservice创建层

  • 无singlton模式
  • 单件模式
  • 在第一种方法中,我在每个新请求中创建新的http客户机对象 通过移动应用程序

    这是我的密码

      public HttpClient GetConnection()
            {
    
                HttpClient httpClient = new HttpClient();
                httpClient.BaseAddress = new Uri(baseAddress); 
                httpClient.Timeout = System.TimeSpan.FromMilliseconds(timeout);
    
    
                return httpClient;
    
            }
    
    邮政编码

     public async Task<TResult> PostAsync<TRequest, TResult>(String url, TRequest requestData)
            {
                HttpClient client = GetConnection();
                String responseData = null;
                if (client != null)
                {
    
                    String serializedObject = await Task.Run(() => JsonConvert.SerializeObject(requestData, _jsonSerializerSettings));
                    var jsonContent = new StringContent(serializedObject, System.Text.Encoding.UTF8, "application/json");
                    HttpResponseMessage response = await client.PostAsync(new Uri(url, UriKind.Relative), jsonContent);
                    responseData = await HandleResponse(response);
    
    
                    return await Task.Run(() => JsonConvert.DeserializeObject<TResult>(responseData, _jsonSerializerSettings));
    
    
                }
                else
                {
    
                    throw new NullReferenceException("NullReferenceException @ PostAsync  httpclient is null WebRequest.cs");
    
                }
    
            }
    
    public Task<HttpResponseMessage> sendData(String url,String jsonData)
            {
    
                var jsonContent = new StringContent(jsonData, System.Text.Encoding.UTF8, "application/json");
    
                return getConnection().PostAsync(new Uri(url, UriKind.Relative), jsonContent);
            }
    
    实现
    IWebRequest

    _webRequest.PostAsync<UserRequest,bool>(Constants.USER_LOGIN, userRequest);
    

    我在web上浏览了许多库,如
    RestSharp
    ,只是为了探索最好的库,我发现大多数库都是根据请求创建新对象的。因此,我不知道哪种模式最适合。

    更新:似乎使用单一的
    HttpClient
    静态实例,因此解决方案是使用
    HttpClientFactory
    。有关Microsoft文档,请参阅

    要使用
    HttpClientFactory
    ,您必须使用Microsoft的依赖项注入。这是ASP.NET核心项目的默认设置,但对于其他项目,您必须引用Microsoft.Extensions.HttpMicrosoft.Extensions.DependencyInjection

    然后,在创建服务容器时,只需调用
    AddHttpClient()

    然后,您可以将
    HttpClient
    注入到您的服务中,而幕后
    HttpClientFactory
    将维护一个
    HttpClientHandler
    对象池-保持DNS的新鲜度并防止出现问题


    旧答案:

    Singleton是使用
    HttpClient
    的正确方法。有关详细信息,请参阅文章

    Microsoft州:

    HttpClient旨在实例化一次,并在应用程序的整个生命周期中重复使用。为每个请求实例化HttpClient类将耗尽重载下可用的套接字数量。这将导致SocketException错误。下面是一个正确使用HttpClient的示例

    事实上,我们在应用程序中发现了这一点。我们的代码可以在
    foreach
    循环中发出数百个API请求,对于每个迭代,我们都使用
    创建一个
    HttpClient
    封装在
    中。很快,我们的
    MongoClient
    就出现了一些令人恼火的错误,说它在尝试连接数据库时超时了。在阅读链接文章后,我们发现即使在处理了
    HttpClient
    之后,也会耗尽可用的套接字

    唯一需要注意的是,诸如
    DefaultRequestHeaders
    BaseAddress
    之类的内容将应用于使用HttpClient的任何地方。作为单例,这可能贯穿整个应用程序。您仍然可以在应用程序中创建多个
    HttpClient
    实例,但请注意,每次创建实例时,它们都会创建一个新的连接池,因此应该谨慎创建


    正如hvaughan3所指出的,您也不能更改HttpClient使用的
    HttpMessageHandler
    的实例,因此如果这对您很重要,您将需要使用该处理程序的单独实例。

    HttpClient
    应该被重用,这并不一定意味着我们必须使用单例来组织代码。请参阅。下文也引述


    我来晚了,但这是我关于这个棘手话题的学习之旅

    1.我们在哪里可以找到重用HttpClient的官方倡导者? 我是说,如果 以及, 这样的倡导者更好地记录在自己的API文档中, 而不是隐藏在许多“高级主题”、“性能(反)模式”中 或者其他的博客文章。 否则,一个新的学习者怎么能在为时已晚之前知道它呢

    截至目前(2018年5月),谷歌搜索“c#httpclient”时的第一个搜索结果 指向,但根本没有提到这个意图。 嗯,新手的第1课是, 始终在MSDN帮助页面标题后单击“其他版本”链接, 您可能会在那里找到指向“当前版本”的链接。 在这个HttpClient案例中,它将为您带来最新的文档

    我怀疑很多对这个话题不熟悉的开发人员 也没有找到正确的文档页, 这就是为什么这些知识没有被广泛传播, 当人们发现这一点时,他们感到惊讶 , 可能吧

    2.使用
    IDisposable
    这一点有点离题,但仍然值得指出的是,看到人并不是巧合 在上面提到的那些博客文章中,他们指责了
    HttpClient
    IDisposable
    接口 使他们倾向于使用(var client=new HttpClient()){…}
    模式 然后引出问题

    我相信这可以归结为一个未被说出的(错误的?)概念:

    然而,当我们以这种风格编写代码时,它看起来确实是一件短命的事情:

    using (var foo = new SomeDisposableObject())
    {
        ...
    }
    
    从不提及IDisposable对象必须是短期的。 根据定义,IDisposable只是一种允许您释放非托管资源的机制。 没别的了。从这个意义上说,你最终会触发处置, 但它并不要求你以一种短命的方式这样做

    因此,您的工作是正确选择何时触发处置, 基于真实对象的生命周期要求。 没有任何东西可以阻止您长期使用IDisposable:

    using System;
    namespace HelloWorld
    {
        class Hello
        {
            static void Main()
            {
                Console.WriteLine("Hello World!");
    
                using (var client = new HttpClient())
                {
                    for (...) { ... }  // A really long loop
    
                    // Or you may even somehow start a daemon here
    
                }
    
                // Keep the console window open in debug mode.
                Console.WriteLine("Press any key to exit.");
                Console.ReadKey();
            }
        }
    }
    
    有了这个新的认识,现在我们再来看看,, 我们可以清楚地注意到,“修复程序”只初始化了一次HttpClient,但从未处理过它, 这就是为什么我们可以从它的netstat输出中看到, 连接保持在已建立状态,这意味着它尚未正确关闭。 如果它被关闭,它的状态将是及时等待。 实际上,在整个程序结束后只泄漏一个打开的连接并不是什么大问题, 还有博客
    HttpService.Instance.sendData(...)
    
    var services = new ServiceCollection();
    services.AddHttpClient()
    var serviceProvider = services.BuildServiceProvider();
    
    using (var foo = new SomeDisposableObject())
    {
        ...
    }
    
    using System;
    namespace HelloWorld
    {
        class Hello
        {
            static void Main()
            {
                Console.WriteLine("Hello World!");
    
                using (var client = new HttpClient())
                {
                    for (...) { ... }  // A really long loop
    
                    // Or you may even somehow start a daemon here
    
                }
    
                // Keep the console window open in debug mode.
                Console.WriteLine("Press any key to exit.");
                Console.ReadKey();
            }
        }
    }
    
    System.InvalidOperationException: Concurrent reads or writes are not supported.\r\n   at System.IO.Pipelines.PipeCompletion.ThrowLatchedException()\r\n   at System.IO.Pipelines.Pipe.GetReadResult(ReadResult& result)\r\n   at System.IO.Pipelines.Pipe.GetReadAsyncResult()\r\n   at Microsoft.AspNetCore.Server.IIS.Core.IISHttpContext.WriteBody(Boolean flush)","ClassName":"IISHttpContext","MethodName":"WriteBody","EventId":{"Id":3,"Name":"UnexpectedError"},"SourceContext":"Microsoft.AspNetCore.Server.IIS.Core.IISHttpServer"