【发布时间】:2019-08-12 13:05:42
【问题描述】:
我在 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 甚至是Running 或WaitingToRun,如果我把它改成...
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));
我一直试图找出我做错了什么,试图改变所有的等待,但我没有成功,所以非常感谢任何帮助。
【问题讨论】:
-
你为什么有.ConfigureAwait(false);
-
@AzharKhorasany 它对任务完成没有任何影响,但是在客户端调用中使用它至少可以进入 WaitingForActivation。没有它,它会返回 Running 甚至 WaitingToRun 状态。我是这方面的新手,所以我正在尝试查看此实现的示例,但我没有找到太多关于同步/异步用法的信息......
-
使用 ConfigureAwait(false) 来避免死锁是一种危险的做法。您必须对阻塞代码调用的所有方法的传递闭包中的每个等待使用 ConfigureAwait(false),包括所有第三方和第二方代码。使用 ConfigureAwait(false) 避免死锁充其量只是一种技巧。
-
在这一行: var response = Client.Post
("v2/Payment", transaction);后面的调用是如何执行的?是在等待 Send (Method.Post, ...) 吗?为什么您的 Client.Post 不是 async/await? -
@AzharKhorasany 我从方法中删除了所有 ConfigureAwait 并将 POST 调用更改为 async/await,但返回相同的 WaitingForActivation。必须同步调用该方法的最终用法,所以这是我的主要问题。如果我将其全部转为 async/await,则方法可以工作并提供预期的结果。有什么方法可以让它在被调用时双向工作?
标签: c# async-await dotnet-httpclient