我敢肯定,许多开发人员在工作中使用“异步/等待”方法。当然,需要处理错误。我将用示例解释如何以正确的方式进行操作。最重要的是,让我们创建一个简单的控制台应用程序:
public static class Program
{
public static async Task Main()
{
var result = await MakeRequestGet();
Console.WriteLine(result);
}
private static async Task<string> MakeRequestGet()
{
var client = new HttpClient();
var result = await client.GetStringAsync("https://www.iana.org/domains/example");
return result;
}
}
您可以看到,我调用异步HTTP请求,对于本文,它将以403代码返回错误。通常,以这种方式的代码没有人写,因为您会得到例外,并且该应用程序将被停止。通常,此类调用包裹在try•catch块中。而且,如果您创建了许多类似的方法,则应将其添加到此块的每种方法中。我认为这项工作使副驾驶员产生了很多可重复的代码。该代码将起作用,但这是不好的做法:
public static class Program
{
public static async Task Main()
{
var result = await MakeRequestGet();
Console.WriteLine(result);
}
private static async Task<string> MakeRequestGet()
{
try
{
var client = new HttpClient();
var result = await client.GetStringAsync("https://www.iana.org/domains/example");
return result;
}
catch (Exception e)
{
//you can use your favorite logger
Console.WriteLine(e);
return string.Empty;
}
}
}
我想改进此代码,使其更优雅,更好。首先,让我们创建自己的结果对象,以保持结果和错误。我创建了非传播和通用类。
public class Result
{
protected readonly Exception? ExceptionError;
public bool Success { get; }
public string Message => ExceptionError?.Message ?? string.Empty;
protected Result(bool success, Exception? exceptionError)
{
Success = success;
ExceptionError = exceptionError;
}
public Exception GetError() => ExceptionError
?? throw new InvalidOperationException($"Error property for this Result not set.");
public static Result Ok => new(true, null);
public static Result Error(Exception error)
{
return new Result(false, error);
}
public static implicit operator Result(Exception exception) =>
new(false, exception);
}
public sealed class Result<T> : Result
where T : class
{
private readonly T? _payload;
private Result(T? payload, Exception? exceptionError, bool success) : base(success, exceptionError)
{
_payload = payload;
}
public Result(T payload) : base(true, null)
{
_payload = payload ?? throw new ArgumentNullException(nameof(payload));
}
private Result(Exception error) : base(false, error)
{
}
public T GetOk() => Success
? _payload ?? throw new InvalidOperationException($"Payload for Result<{typeof(T)}> was not set.")
: throw new InvalidOperationException($"Operation for Result<{typeof(T)}> was not successful.");
public new Exception GetError() => ExceptionError
?? throw new InvalidOperationException($"Error property for Result<{typeof(T)}> not set.");
public new static Result<T> Ok(T payload)
{
return new Result<T>(payload, null, true);
}
public new static Result<T> Error(Exception error)
{
return new Result<T>(null, error, false);
}
public static implicit operator Result<T>(T payload) =>
new(payload, null, true);
public static implicit operator Result<T>(Exception exception) =>
new(exception);
}
下一步,让我们创建一个助手来处理任务。还有两个版本的处理程序 - 非传播和通用。
public static class Helper
{
public static async Task<Result> TryAwait(
this Task task,
Action<Exception> errorHandler = null
) {
try {
await task;
return Result.Ok;
}
catch (Exception ex)
{
if (errorHandler is not null) errorHandler(ex);
return ex;
}
}
public static async Task<Result<T>> TryAwait<T>(
this Task<T> task,
Action<Exception> errorHandler = null
) where T : class {
try {
return await task;
}
catch (Exception ex)
{
if (errorHandler is not null) errorHandler(ex);
return ex;
}
}
}
让我们检查一下并重写我们的方法:
private static async Task<Result> MakeRequestGet()
{
var client = new HttpClient();
var result = await client.GetStringAsync("https://www.iana.org/domains/example").TryAsync();
return result;
}
,还对主要方法进行了较小的更改:
public static async Task Main()
{
var result = await MakeRequestGet();
Console.WriteLine(result.Message);
}
最后,您将检索与以前的代码相同的结果,但是没有尝试仅创建一次的catch块。您还可以添加自己喜欢的记录仪,但是您需要在代码中进行一些更改:
private static async Task<Result> MakeRequestGet()
{
var client = new HttpClient();
var result = await client.GetStringAsync("https://www.iana.org/domains/example").TryAsync(x =>
{
Console.WriteLine(x.Source);
});
return result;
}
您可以看到,此代码更可读和清洁。另外,您可以根据需要更改结果类。
作为奖励,如果您不需要回调,我想展示如何处理void方法。但是,在空隙方法中也可能是例外,您应该处理它。让我们返回到帮手类,添加另一种方法。在这种方法中,我得到了处理异常的ilogger。
public static async Task TryAwait(
this Task task,
ILogger logger = null
)
{
try
{
await task;
}
catch (Exception ex)
{
if(logger is not null) logger.Log(LogLevel.Error, ex.Message);
}
}
我们需要安装软件包并进行注册以使用此扩展名。让我们添加此代码:
private static ILogger<Program>? _logger;
public static async Task Main()
{
SetLogger();
await MakeRequestGet();
}
private static void SetLogger()
{
var services = new ServiceCollection();
services.AddLogging(builder =>
{
builder.AddConsole();
});
var serviceProvider = services.BuildServiceProvider();
_logger = serviceProvider.GetService<ILogger<Program>>();
}
,还需要对此方法进行一些更改:
private static async Task MakeRequestGet()
{
var client = new HttpClient();
await client.GetStringAsync("https://www.iana.org/domains/example").TryAwait(_logger);
}
您可以看到,我将记录器调用到Tryawait方法中。当抛出异常时,记录器会显示您的消息。当然,您可以为您需要更改它。
仅此而已。我希望有人会有用。愉快的编码!