我在使用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
甚至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));
我一直在试图找出自己在做错什么,试图改变所有等待者,但是我对此并不满意,因此我们将不胜感激。
答案 0 :(得分:1)
我怀疑您的问题在这里
public object Credit(Transaction transaction)
{
var response = Client.Post<Transaction>("v2/Payment", transaction);
return response;
}
您没有显示Post<T>()
的代码,但我认为它也是一个async Task<T>
方法,这意味着response
是Task<T>
,并且您的代码基本上在做这个:
当我认为这确实是您想要的时候:
理想情况下,这应该是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