如何正确地将异步http请求包装成成功并使回调失败

问题描述 投票:0回答:1

我正在重构执行同步http请求的旧代码,并返回带有成功和失败事件的Callback对象。如何正确地将代码包装到async / await中?

我已经添加了HttpClient类,我正在使用我等待的SendAsync方法,但我不确定如何正确地从await转换为事件。我在类中添加了异步void Execute方法,但它似乎不是正确的处理方式 - 避免异步void。下面是(简短版)代码中的更多解释。


public class HttpExecutor(){

    public event Action<string> Succeed;
    public event Action<ErrorType, string> Failed;
    private bool isExecuting;

    //I know that async void is not the best because of exceptions
    //and code smell when it is not event handler
    public async void Execute()
        {
            if (isExecuting) return;

            isExecuting = true;
            cancellationTokenSource = new CancellationTokenSource();

            try
            {
                httpResponseMessage =
                    await httpService.SendAsync(requestData, cancellationTokenSource.Token).ConfigureAwait(false);

                var responseString = string.Empty;
                if (httpResponseMessage.Content != null)
                {
                    responseString = await httpResponseMessage.Content.ReadAsStringAsync().ConfigureAwait(false);
                }

                if (httpResponseMessage.IsSuccessStatusCode)
                {
                    Succeed?.Invoke(responseString);
                    return;
                }

                Failed?.Invoke(httpResponseMessage.GetErrorType(),
                    $"{httpResponseMessage.ReasonPhrase}\n{responseString}");
            }
            //Catch all exceptions separately
            catch(...){
            }
            finally
            {
                Dispose();
            }
        }
}

public class UserService(){

    public CallbackObject<User> GetUser(){
        var executor = new HttpExecutor(new RequestData());
        //CallbackObject has also success and fail, and it hooks to executor events, deserializes string into object and sends model by his own events.
        var callback = new CallbackObject<User>(executor);
        executor.Execute();//in normal case called when all code has possibility to hook into event
        return callback;
    }

}

我觉得我应该将方法更改为:public async Task ExecuteAsync(){...}然后我需要从线程池中获取线程:Task.Run(()=>executor.ExecuteAsync());

看起来它有点火和忘记,但有回调(我等待网络的响应)。如何妥善处理?

c# asynchronous async-await request void
1个回答
2
投票

我正在重构执行同步http请求的旧代码,并返回带有成功和失败事件的Callback对象。如何正确地将代码包装到async / await中?

你完全摆脱了回调。

首先,考虑失败案例。 (ErrorType, string)应该成为一个自定义Exception

public sealed class ErrorTypeException : Exception
{
  public ErrorType ErrorType { get; set; }

  ...
}

然后你可以将Succeed / Failed回调模型化为单个Task<string>

public async Task<string> ExecuteAsync()
{
  if (isExecuting) return;
  isExecuting = true;

  cancellationTokenSource = new CancellationTokenSource();
  try
  {
    httpResponseMessage = await httpService.SendAsync(requestData, cancellationTokenSource.Token).ConfigureAwait(false);
    var responseString = string.Empty;
    if (httpResponseMessage.Content != null)
    {
      responseString = await httpResponseMessage.Content.ReadAsStringAsync().ConfigureAwait(false);
    }

    if (httpResponseMessage.IsSuccessStatusCode)
      return responseString;

    throw new ErrorTypeException(httpResponseMessage.GetErrorType(),
        $"{httpResponseMessage.ReasonPhrase}\n{responseString}");
  }
  catch(...){
    throw ...
  }
  finally
  {
    Dispose();
  }
}

用法:

public Task<User> GetUserAsync()
{
  var executor = new HttpExecutor(new RequestData());
  var text = await executor.ExecuteAsync();
  return ParseUser(text);
}
© www.soinside.com 2019 - 2024. All rights reserved.