为什么使用HttpClient进行同步连接

Ket*_*hup 173 c# asp.net dotnet-httpclient

我正在构建一个类API来与API进行交互.我需要调用API并处理XML响应.我可以看到HttpClient用于异步连接的好处,但我正在做的是纯粹的同步,所以我看不到使用它的任何重大好处HttpWebRequest.

如果有人可以放弃任何光线,我将非常感激.我不是为了它而使用新技术的人.

Dar*_*rov 349

但我正在做的是纯粹的同步

您可以使用HttpClient同步请求就好了:

using (var client = new HttpClient())
{
    var response = client.GetAsync("http://google.com").Result;

    if (response.IsSuccessStatusCode)
    {
        var responseContent = response.Content; 

        // by calling .Result you are synchronously reading the result
        string responseString = responseContent.ReadAsStringAsync().Result;

        Console.WriteLine(responseString);
    }
}
Run Code Online (Sandbox Code Playgroud)

至于为何你应该使用HttpClient而不是WebRequest,那么,HttpClient是块中的新手,可能包含对旧客户端的改进.

  • 根据这篇文章(http://blogs.msdn.com/b/pfxteam/archive/2012/04/13/10293638.aspx),调用`.Result`这样会耗尽线程池并导致死锁. (32认同)
  • 您是否同步使用异步方法可能会阻止您的UI线程?你可能想要考虑类似`string responseString = Task.Run(()=> responseContent.ReadAsStringAsync()).结果;`相反,如果你_must_使这个同步. (24认同)
  • 那么,如何从UI线程同步使用HttpClient?让我说我故意**想要阻止UI线程(或者我正在编写一个控制台应用程序),直到我得到HTTP响应......所以,如果Wait(),Result()等会导致死锁,那么肯定是什么这个**的解决方案没有**死锁的风险和**没有**使用像WebClient这样的其他类? (19认同)
  • 如果在主UI线程上创建的任务中执行`new TaskFactory(TaskScheduler.FromCurrentSynchronizationContext()),此代码将始终死锁. (16认同)
  • @earthling,是的,`Task.Run`从ThreadPool调用任务,但你在它上面调用`.Result`从这里杀掉所有的好处并阻塞你称之为`.Result`的线程(通常会发生)成为主要的UI线程). (11认同)
  • @DarinDimitrov我相信您已经真诚地写了这个答案。但是现在,六年之后,我们知道这段代码是HttpClient可能的每个反模式的集合。此代码可能会给某人带来严重麻烦。您能否考虑删除它或提出免责声明或采取其他措施来警告人们。 (5认同)
  • @DarinDimitrov不应该是`var response = client.GetAsync("http://google.com").GetAttiter().GetResult();`和`string responseString = responseContent.ReadAsStringAsync().GetAwaiter().调用getResult();` (4认同)
  • 上帝保佑你兄弟 (4认同)
  • 这不是一个好的解决方案.你不应该处理HttpClient(是的,即使它继承自IDisposable).此外,这可能会导致死锁(所以我从多个来源读取).有这样的解决方案(https://cpratt.co/async-tips-tricks/)但它不会被阻止.我建议使用允许同步调用的客户端,即使这正是您不想做的事情.唯一剩下的就是上面的解决方案.虽然,您应该修改为不配置并创建静态客户端. (3认同)
  • 此代码包含死锁和反模式(处理 HttpClient)。在修复之前,所有人都应该否决它。 (2认同)

小智 26

我会重复Donny V.回答和Josh的回答

"我不使用异步版本的唯一原因是,如果我试图支持尚未内置异步支持的旧版.NET."

(并且如果我有声誉的话,请进行投票.)

我不记得最后一次,如果有的话,我很感激HttpWebRequest为状态代码> = 400抛出异常这一事实.要解决这些问题,你需要立即捕获异常,并将它们映射到一些非异常响应机制在你的代码中......无聊,乏味且容易出错.无论是与数据库进行通信,还是实现定制的Web代理,它"几乎"总是希望Http驱动程序只是告诉您的应用程序代码返回的内容,并由您决定如何操作.

因此HttpClient是优选的.

  • 有很多很好的理由,例如不想只对非常低级的http调用重写整个代码库,而HTTP调用甚至对性能都不是至关重要的(但是会引入异步到一百万个位置)。 (3认同)

Don*_* V. 17

我使用HttpClient的一个主要原因是因为它在URL上返回404时不会抛出异常.


Lea*_*ura 10

public static class AsyncHelper  
{
    private static readonly TaskFactory _taskFactory = new
        TaskFactory(CancellationToken.None,
                    TaskCreationOptions.None,
                    TaskContinuationOptions.None,
                    TaskScheduler.Default);

    public static TResult RunSync<TResult>(Func<Task<TResult>> func)
        => _taskFactory
            .StartNew(func)
            .Unwrap()
            .GetAwaiter()
            .GetResult();

    public static void RunSync(Func<Task> func)
        => _taskFactory
            .StartNew(func)
            .Unwrap()
            .GetAwaiter()
            .GetResult();
}
Run Code Online (Sandbox Code Playgroud)

然后

AsyncHelper.RunSync(() => DoAsyncStuff());
Run Code Online (Sandbox Code Playgroud)

如果您使用该类将异步方法作为参数传递,则可以安全地从同步方法中调用异步方法。

它在这里解释:https : //cpratt.co/async-tips-tricks/


ale*_*exs 10

对于现在遇到此问题的任何人,.NET 5.0 已将同步Send方法添加到HttpClient. https://github.com/dotnet/runtime/pull/34948

优点也是为什么在这里详细讨论 - https://github.com/dotnet/runtime/issues/32125

因此,您可以使用 this 而不是SendAsync。例如

public string GetValue()
{
    var client = new HttpClient();
            
    var webRequest = new HttpRequestMessage(HttpMethod.Post, "http://your-api.com")
    {
        Content = new StringContent("{ 'some': 'value' }", Encoding.UTF8, "application/json")
    };

    var response = client.Send(webRequest);

    using var reader = new StreamReader(response.Content.ReadAsStream());
            
    return reader.ReadToEnd();
}
Run Code Online (Sandbox Code Playgroud)

这段代码只是一个简化的例子,它不是生产就绪的。


Jos*_*ton 7

如果您正在构建类库,那么您的库的用户可能希望异步使用您的库.我认为那是最大的原因.

您也不知道您的图书馆将如何使用.也许用户将处理大量的请求,并且异步执行这些操作将有助于它更快,更高效地执行.

如果你可以这样做,尽量不要让你的库的用户负担,试图让流程异步,你可以为它们处理它.

我不使用异步版本的唯一原因是,如果我试图支持尚未内置异步支持的旧版.NET.


Dar*_*ekt 6

在我的情况下,接受的答案不起作用.我从一个没有异步操作的MVC应用程序调用API.

这就是我设法让它工作的方式:

private static readonly TaskFactory _myTaskFactory = new TaskFactory(CancellationToken.None, TaskCreationOptions.None, TaskContinuationOptions.None, TaskScheduler.Default);
public static T RunSync<T>(Func<Task<T>> func)
    {           
        CultureInfo cultureUi = CultureInfo.CurrentUICulture;
        CultureInfo culture = CultureInfo.CurrentCulture;
        return _myTaskFactory.StartNew<Task<T>>(delegate
        {
            Thread.CurrentThread.CurrentCulture = culture;
            Thread.CurrentThread.CurrentUICulture = cultureUi;
            return func();
        }).Unwrap<T>().GetAwaiter().GetResult();
    }
Run Code Online (Sandbox Code Playgroud)

然后我这样打电话:

Helper.RunSync(new Func<Task<ReturnTypeGoesHere>>(async () => await AsyncCallGoesHere(myparameter)));
Run Code Online (Sandbox Code Playgroud)