在同步类库中使用HttpClient

时间:2018-01-28 18:15:22

标签: c# .net asynchronous dotnet-httpclient .net-standard

我正在为第三方API编写类库包装器,使用.Net Standard,稍后我计划在我的其他项目中使用这个包装器。在环顾网络时,我发现一般关注的是应该使用HttpClient类来发出HTTP请求。

我知道我可以采取两种方法:

  1. 使用async / await一直到客户端项目
  2. 使用Task.Wait()方法
  3. 到目前为止,我要采用第一种方法。但这两种方法似乎都存在问题。第一个将比具有同步方法的可重用性更低,这些方法返回其类型而不是Task对象。我宁愿采取第二种方法,但它很容易陷入僵局。

    我发出单个请求的代码(使用无参数构造函数初始化HttpClient):

        protected async Task<JObject> Request(string url)
        {
            Uri uri = BuildUrl(url);
            HttpResponseMessage response = await HttpClient.GetAsync(uri);
            if (response.IsSuccessStatusCode)
            {
                string result = await response.Content.ReadAsStringAsync();
                return JObject.Parse(result);
            }
    
            return new JObject();
        }
    

    对于多个请求:

        protected async Task<JObject[]> Request(IEnumerable<string> urls)
        {
            var requests = urls.Select(Request);
            return await Task.WhenAll(requests);
        }
    

    在包装类中的用法:

        protected async Task<JObject> RequestGet(string id, bool byUrl)
        {
            if (IsBulk && !byUrl)
                return await Request($"{Url}?id={id}");
    
            if (byUrl)
                return await Request(Url + id);
    
            return await Request(Url);
        }
    

    我如何修改代码(第1和第2个代码段),以便它不会在每个调用方法(第3个代码段)中导致任何死锁和异步使用?

2 个答案:

答案 0 :(得分:2)

不支持同步使用HttpClient并且容易出现死锁,并且您无法做任何事情(包括使用Task.Wait)来改变这一事实。您有两个选择:

  1. 仅支持异步。

  2. 使用较早的WebRequest APIs并支持同步调用。

  3. 我会选择选项1.有一个原因是最新的和最好的HTTP库甚至不再支持同步I / O.更新一波的多核处理器和分布式架构引发了编程世界的范式转变,在等待I / O(特别是长时间运行的网络调用,如HTTP)的情况下捆绑线程是完全浪费资源。当像C#这样的语言为开箱即用的异步编程提供了令人难以置信的支持时,几乎没有理由再做同步HTTP了。大多数开发人员都明白这一点我认为你不应该只关注通过异步来放弃用户。相反,鼓励他们以正确的方式加快速度。

答案 1 :(得分:0)

一直使用async是正确的方法,你可以从Task方法返回结果(换句话说 - 正如你自己的代码所示 - 它是不对的说他们不会返回任务以外的任何东西:

public async Task<string> GetString(int value)
{
    return value.ToString();
}

显然,不需要async,它不需要await任何东西,但由于泛型,点Task可以返回任何内容。你甚至可以使用花哨的新C#7值元组:

public async Task<(string name, int age)> GetUserInfo(int userId) { ... }