异步/同步实现中的HttpClient返回WaitingForActivation

时间:2019-08-12 13:05:42

标签: c# async-await dotnet-httpclient

我在使用sync同步HttpClient的实现时遇到问题。

Id = 8, Status = WaitingForActivation, Method = "{null}", Result = "{Not yet computed}"

我知道我在做什么可能是一个不好的做法,使所有路径异步是理想的,但这是公司要求我这样做的要求,所以我必须这样做。

项目是在NET Standard 1.1中构建的,可用作NuGet程序包,并且也与Framework和Core兼容。

这是我的主要客户群...

private static HttpClient _client;
private static Uri _baseAddress;
private static readonly JsonSerializerSettings _settings = new JsonSerializerSettings
    { DefaultValueHandling = DefaultValueHandling.Ignore, NullValueHandling = NullValueHandling.Ignore, MissingMemberHandling = MissingMemberHandling.Ignore };

public Client() { }

private Client(string baseUrl, Config config)
{
    _baseAddress = new Uri(baseUrl);

    _client = new HttpClient { Timeout = TimeSpan.FromSeconds(config.Timeout) };

    _client.DefaultRequestHeaders.Accept.Clear();
    _client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
    _client.DefaultRequestHeaders.Add("X-API-KEY", config.Token);
}

private Client _paymentClient;
private Client _mainClient;

public Client Create(bool payment, Config config = null)
{
    if (!payment)
    {
        _mainClient = _mainClient ?? new Client("https://api.address.com/", config);
        return _mainClient;
    }

    _paymentClient = _paymentClient ?? new Client("https://payment.address.com/", config);
    return _paymentClient;
}

public void Dispose() => _client.Dispose();

private static async Task<T> Send<T>(HttpMethod method, string url, object data = null)
{
    var uri = new UriBuilder(_baseAddress);
        uri.Path += url;

    var request = new HttpRequestMessage(method, uri.Uri);

    if (data != null)
        request.Content = new StringContent(JsonConvert.SerializeObject(data, _settings), Encoding.UTF8, "application/json");

    var response = await _client.SendAsync(request).ConfigureAwait(false);
    response.EnsureSuccessStatusCode();

    var content = await response.Content.ReadAsStringAsync();

    T result = default;

        if (response.IsSuccessStatusCode)
        {
            if (response.Content.Headers.ContentType.MediaType == "application/json")
            {
                var responseObj = JsonConvert.DeserializeObject<Response<T>>(content, _settings);

                if (responseObj.HasError)
                    throw new Safe2PayException(responseObj.ErrorCode, responseObj.Error);

                responseObj.ResponseDetail = result;
            }
        }
        else throw new Exception((int) response.StatusCode + "-" + response.StatusCode);

    request.Dispose();
    response.Dispose();

    return result;
}

Send<T>方法被认为是处理请求和响应的一般方法,包装在这样的通用调用上:

internal Task<T> Get<T>(string url) => Send<T>(HttpMethod.Get, url);

//OR even async...

internal async Task<T> Get<T>(string url) => await Send<T>(HttpMethod.Get, url);

这样被称为发送和接收数据。

private Client Client { get; }

public CheckoutRequest(Config config) => Client = new Client().Create(true, config);

public object Credit(Transaction transaction)
{
    var response = Client.Post<Transaction>("v2/Payment", transaction);
    return response;
}

我的问题是客户总是会给我一个WaitingfForActivation甚至RunningWaitingToRun,如果我将其更改为...也没关系。

Task.Run(() => Send<T>(HttpMethod.Get, url));
//or
Task.Run(() => Send<T>(HttpMethod.Get, url).Result);
//or
Task.Run(async () => await Send<T>(HttpMethod.Get, url));
//or
Task.Run(async () => await Send<T>(HttpMethod.Get, url).ConfigureAwait(false));

我一直在试图找出自己在做错什么,试图改变所有等待者,但是我对此并不满意,因此我们将不胜感激。

1 个答案:

答案 0 :(得分:1)

我怀疑您的问题在这里

public object Credit(Transaction transaction)
{
    var response = Client.Post<Transaction>("v2/Payment", transaction);
    return response;
}

您没有显示Post<T>()的代码,但我认为它也是一个async Task<T>方法,这意味着responseTask<T>,并且您的代码基本上在做这个:

  1. 开始任务。
  2. 返回未完成任务的描述。

当我认为这确实是您想要的时候:

  1. 开始任务。
  2. 等待任务完成。
  3. 返回任务结果。

理想情况下,这应该是async方法,您可以await执行任务:

public async Task<object> Credit(Transaction transaction)
{
    var response = await Client.Post<Transaction>("v2/Payment", transaction);
    return response;
}

如果您绝对必须同步等待任务(几乎没有必要这样做),则可以使用.GetAwaiter().GetResult()

public object Credit(Transaction transaction)
{
    var response = Client.Post<Transaction>("v2/Payment", transaction).GetAwaiter().GetResult();
    return response;
}

使用.GetAwaiter().GetResult()代替.Result的主要好处是,在出现异常的情况下,它将引发 actual 异常而不是AggregateException

此外,您可以将Create()方法设为static

public static Client Create(bool payment, Config config = null)

然后,您无需初始化类就可以调用它:

public CheckoutRequest(Config config) => Client = Client.Create(true, config);

更新:如果要使用同一方法的async和非异步版本,则可以遵循Microsoft使用的相同标准,并用{后缀async。非异步版本只能调用异步版本。例如:

Async