在2014年,微软宣布他们打算发布一个开源的.NET Framework的继任者:.NET Core。这是一个重大举措,不久之后,.NET的源代码被发布到了GitHub上。宣布.NET Core将成为所有未来.NET版本的基础,并由.NET基金会指导开源贡献。
.NET Core 成为了一个巨大的成功。随着 2020 年发布 .NET 5,.NET Framework 和 .NET Core 被合并成一个单一的、开源的、跨平台的技术。
转向开源促进了围绕.NET平台的繁荣社区的发展。许多有才华的人发布了高质量的工具和库,让开发者的每一天都变得更加轻松。
我非常支持开源,并且更支持不重复发明轮子。在这篇文章中,我将介绍4个我绝对离不开的最佳库。大多数时候,我开始任何新项目时都会安装这些库。
1. Refit在 .NET 中处理 HTTP 请求传统上需要编写大量的手动和样板代码。开发人员必须手动处理构造 HTTP 请求的细节,处理头部信息,管理响应并执行反序列化。
这通常通过某种自定义实现的 HttpClient
类来完成:
public class Program
{
private static readonly HttpClient client = new HttpClient();
public static async Task Main(string[] args)
{
var user = await GetUserAsync(123);
Console.WriteLine($"用户名称: {user.Name}");
}
private static async Task<User> GetUserAsync(int userId)
{
// 设置基础地址和默认头信息(如果需要)
client.BaseAddress = new Uri("https://api.example.com/");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
// 发送 HTTP 请求
var response = await client.GetAsync($"users/{userId}");
response.EnsureSuccessStatusCode(); // 如果状态码不是200-299则抛出异常
// 反序列化响应体
var responseBody = await response.Content.ReadAsStringAsync();
return JsonConvert.DeserializeObject<User>(responseBody);
}
}
它可能显得繁琐、重复且难以阅读。
相反,Refit 会为你处理这一切,将你的 REST API 转换为实时接口。
简单地定义你的接口:
public interface IMyApi
{
[Get("/users/{userId}")]
Task<User> GetUserAsync(int userId);
}
在需要的地方注入该服务,并使用它来发起 HTTP 请求。
public class 用户控制器 : ControllerBase
{
private readonly IMyApi _myApi;
public 用户控制器(IMyApi myApi)
{
_myApi = myApi;
}
[HttpGet("{id}")]
public async Task<IActionResult> 获取用户(int id)
{
var 用户 = await _myApi.GetUserAsync(id);
return Ok(用户);
}
}
背后,Refit 会使用所有最佳实践和标准为你生成必要的代码。你只需要定义接口,其余的都会由 Refit 自动处理。
所有你的HTTP调用都被分离成简单且易于维护的模块,避免了冗余代码。序列化和反序列化由框架自动处理,你的API调用在编译时会进行类型检查。这意味着你可以提前捕获潜在的错误,从而避免在运行时出现这些问题。Refit还具有高度的可定制性,可以轻松地调整以处理自定义头、参数等。
2. CoravelCoravel 是一个出色的库,它使得像调度、队列、缓存、后台任务和事件广播这样的繁重任务变得极其简单和直观。Coravel 是一个庞大的库,涵盖了多个不同的领域。
具体来说,我喜欢使用它来处理定时任务的功能。你很少会构建一个不需要处理某种重复任务的系统。也许你需要每小时将数据导出到第三方系统一次,或者可能需要在每天凌晨12点备份数据库。Coravel 提供了一种几乎无需配置的方式来处理这类任务,并且比主要云提供商提供的解决方案更易于维护。
只需定义一个继承自 IInvocable
接口的任务。在 Coravel 中,一个可调用对象只是表示一个 Coravel 可以在应用程序的不同部分使用的特定任务。这里就是你放置希望按计划运行的实际业务逻辑的地方。
使用 Coravel.Invocable;
使用 System;
使用 System.Threading.Tasks;
公类 MyScheduledTask 实现 IInvocable
{
公共异步任务 Invoke()
{
// 在这里编写你的逻辑
写入控制台($"正在执行计划任务,时间:{DateTime.Now}");
返回;
}
}
现在只需在你的 Program.cs 中注册 Coravel 定时器服务,并指定你希望任务何时以及多久运行一次。
使用 Coravel;
使用 Microsoft.AspNetCore.Builder;
使用 Microsoft.Extensions.DependencyInjection;
使用 Microsoft.Extensions.Hosting;
var builder = WebApplication.CreateBuilder(args);
// 将服务添加到容器中。
builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
// 添加 Coravel 服务
builder.Services.AddScheduler();
// 注册可调用任务
builder.Services.AddTransient<MyScheduledTask>();
var app = builder.Build();
// 配置 HTTP 请求管道。
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
// 安排可调用任务
var provider = app.Services;
provider.UseScheduler(scheduler =>
{
scheduler.Schedule<MyScheduledTask>().EveryMinute();
});
app.Run();
完成。在幕后,Coravel 会自动生成并处理运行你任务所需的所有代码,并按照你设定的时间间隔执行。甚至还可以接受 CRON 表达式,以满足你的需求。
确保在 Github 上给 Coravel 点赞。
3. FluentValidationFluentValidation 是一个流行的 .NET 库,它简化了在 .NET 应用程序中定义和强制执行验证规则的过程。传统上,在 .NET 中进行数据验证是一项繁重的任务,需要你要么在类中添加自定义逻辑,要么使用不灵活的数据注解。
相反,FluentValidation 允许开发人员通过提供一个接口来分离编写验证逻辑的职责,该接口支持可自定义的验证实现。开发人员不再需要编写冗长且重复的验证代码,这些代码很快变得难以管理。FluentValidation 的接口允许你以类似于自然语言的方式定义验证规则。这极大地提高了代码的可读性,并使未来的开发人员更容易理解和维护代码。
首先定义一个继承自 AbstractValidator 的类,并使用 RuleFor 方法简单地定义你的验证规则。
public class 客户
{
public string 名称 { get; set; }
public int 年龄 { get; set; }
public string 邮箱 { get; set; }
}
public class 客户验证器 : AbstractValidator<客户>
{
public 客户验证器()
{
RuleFor(customer => customer.名称)
.NotEmpty().WithMessage("名称是必填的。");
RuleFor(customer => customer.年龄)
.InclusiveBetween(18, 60).WithMessage("年龄必须在18到60之间。");
RuleFor(customer => customer.邮箱)
.EmailAddress().WithMessage("无效的电子邮件地址。");
}
}
FluentValidation 提供了许多方便的预定义验证方法,例如 .EmailAddress()、.NotEmpty()、.GreaterThan() 和 .CreditCard(),但也可以处理您自己的自定义验证逻辑。
一旦定义,CustomerValidator 就可以在需要的地方用于验证任何客户对象。
var customer = new Customer { Name = "", Age = 25, Email = "invalid-email" };
var validator = new CustomerValidator();
var result = validator.Validate(customer);
if (!result.IsValid)
{
foreach (var failure in result.Errors)
{
Console.WriteLine($"属性 {failure.PropertyName} 验证失败。错误: {failure.ErrorMessage}");
}
}
4. Polly
Polly 是一个流行的弹性处理和故障处理库,可以显著提高应用程序的健壮性。它允许开发人员定义不同的策略,以实现重试逻辑、断路器、超时、隔离区和回退功能,从而提高软件的可靠性。
在编写软件时,异常和临时错误不可避免地会发生。例如,你的程序在其生命周期中的某个时刻不可避免地会遇到网络超时的情况。如果没有优雅的方式来处理这些情况,你的应用程序的关键业务方面可能会受到影响。这就是 Polly 发挥作用的地方。Polly 中的策略高度模块化,可以组合使用以解决最复杂的错误处理场景。
例如,你可以结合重试策略、断路器和回退策略来创建一个健壮的故障处理策略。
假设你有一个从外部API获取数据的服务,并且你想让这个调用能够抵御瞬时故障:
using System;
using System.Net.Http;
using System.Threading.Tasks;
using Polly;
using Polly.CircuitBreaker;
using Polly.Fallback;
using Polly.Retry;
class Program
{
static async Task Main(string[] args)
{
// 定义重试策略
var retryPolicy = Policy
.HandleResult<HttpResponseMessage>(r => !r.IsSuccessStatusCode)
.WaitAndRetryAsync(3, retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt)));
// 定义断路器策略
var circuitBreakerPolicy = Policy
.HandleResult<HttpResponseMessage>(r => !r.IsSuccessStatusCode)
.CircuitBreakerAsync(3, TimeSpan.FromSeconds(30));
// 定义回退策略
var fallbackPolicy = Policy<HttpResponseMessage>
.Handle<BrokenCircuitException>()
.OrResult(r => !r.IsSuccessStatusCode)
.FallbackAsync(
new HttpResponseMessage(System.Net.HttpStatusCode.OK)
{
Content = new StringContent("{\"message\": \"Fallback response\"}")
},
onFallbackAsync: async b =>
{
Console.WriteLine("执行回退逻辑...");
});
// 组合策略
var combinedPolicy = fallbackPolicy.WrapAsync(circuitBreakerPolicy).WrapAsync(retryPolicy);
using (var httpClient = new HttpClient())
{
var request = new HttpRequestMessage(HttpMethod.Get, "https://external-api.com/data");
try
{
// 使用组合策略执行请求
HttpResponseMessage response = await combinedPolicy.ExecuteAsync(() => httpClient.SendAsync(request));
if (response.IsSuccessStatusCode)
{
Console.WriteLine("请求成功!");
// 处理响应
string data = await response.Content.ReadAsStringAsync();
Console.WriteLine(data);
}
else
{
Console.WriteLine("请求失败,状态码:" + response.StatusCode);
}
}
catch (Exception ex)
{
Console.WriteLine("发生异常:" + ex.Message);
}
}
}
}
我们首先定义所需的三个策略;一个重试策略,一个断路器策略和一个回退策略。
重试策略将在遇到非成功状态码时尝试最多重试失败的 HTTP 请求三次。重试之间采用指数退避策略,这意味着每次操作失败后,重试之间的时间间隔会增加。
断路器策略通过在连续三次失败尝试后停止执行来工作。一旦发生这种情况,系统将有30秒的时间进行恢复,之后才会再次尝试。
最后,如果所有重试都失败并且断路器仍然处于打开状态,我们将使用回退策略来处理一个默认响应,以确保应用程序可以继续运行。
当把这些组件组合在一起时,我们就有了一个非常强大的系统来处理在执行HTTP请求期间可能出现的任何故障或异常。