.NET 6,微软称为“最快的.NET”,带有了许多令人兴奋的新功能、语言和性能改进。这是自 .NET Core 3.1 以来的第一个 LTS 版本,将支持三年。本次大版本发布,增加了一个新特性:Minimal APIs,这是什么技术?
.NET6 使编写具有最小依赖性的 REST API 变得非常简单。
Minimal APIs 似乎是微软对 NodeJS(使用 ExpressJS)HTTP 服务器的回应,它提供了最小的 API。但是微软也对这项技术增加了几个关键词:
总结一句话:.NET 6 Minimal APIs 简化了 HTTP Rest API 的设计和实现,让开发者快速高效实现HTTP Rest API。
今天,我们花点时间,研究并科普一下.NET 6 Minimal APIs。
先看一下.NET 6 Minimal APIs的示例代码
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("/", () => "Hello World!");
app.Run();
在上面的示例中,app.MapGet 方法使用了内联 lambda 表达式,完成一个 Controller Action 的业务逻辑,真的是超简单。
超简单完成一个 HTTP WebAPI 的定义:不再有 Startup.cs、API 控制器、额外依赖项等。
只需要这 4 行代码即可生成以下输出:
探究一下这段代码背后的一些技术
上面的代码,微软官方文档上,建议大家使用VS2022,其实用VS Code也可以
Tutorial: Create a minimal web API with ASP.NET Core
但是本机得先安装.NET 6 SDK
安装完成后,打开VS Code,新建终端,创建一个Web Project
dotnet new web -o MyMinimalAPI
代码工程中,我们可以看到:
Program.cs这个类中没有using了,当然也没有main函数了,这里跟大家解释一下:
① .NET5 引入了Top-Level Class,可以没有main函数,代码作为直接入口执行
② .NET 6 新增了一个很棒的新特性——“隐式全局使用”
自动生成不可见的 using 语句并在全局范围内声明它们,因此不必处理在每个文件中重复声明命名空间的混乱。
我们打开MyMinimalAPI.csproj 看看里面的内容,有一个配置:
dotnet build 后,找到 obj/Debug/net6.0 文件夹以查看隐藏的自动生成文件 - [ProjectName].GlobalUsings.g.cs。使用一个单独的类来将所有 using 语句保存在一个地方。
这个功能,让我们不需要在每个文件中重复声明命名空间的 using 引用了。的确很方便、简单了。
当然,如果不想使用此功能,可以禁用 .csproj 文件中的 ImplicitUsings 标志。
在上面的示例中,app.MapGet 方法使用了内联 lambda 表达式。同时还提供了:
app.MapPost()
app.MapPut()
app.MapDelete()
接下来,我们用一个简单的示例,完成一个 demo。
完成一个 Minimal APIs 完整的 Demo
我们以一个简单 Order 订单为例,通过 Minimal APIs 实现 CRUD 设计和实现:
▌先准备好 Order 类和 IOrderService 接口以及 OrderServiceRepository
Order 类:
namespace NET6
{
public class Order
{
public int ID {get;set;}
public decimal Price {get;set;}
public string CustomAddress {get;set;}
public int State{get;set;}
}
}
IOrderService 接口:
namespace NET6
{
public interface IOrderService
{
Order GetOrder(int id);
void CreateOrder(Order order);
void DeleteOrder(int id);
void UpdateOrder(Order order);
}
}
OrderServiceRepository 类,使用内存集合模拟实现数据存储层。
namespace NET6
{
public class OrderServiceRepository : IOrderService
{
static List orders = new List();
public Order GetOrder(int id)
{
return orders.FirstOrDefault(i=>i.ID == id)?? null;
}
public void CreateOrder(Order order)
{
orders.Add(order);
}
public void DeleteOrder(int id)
{
var order = orders.FirstOrDefault(i=>i.ID == id);
if(order!=null)
orders.Remove(order);
}
public void UpdateOrder(Order order)
{
DeleteOrder(order.ID);
CreateOrder(order);
}
}
}
▌在 Program.cs 类中,注册 IOrderService 服务,添加 AddOrder 和 GetOrder Http Web API
using NET6;
var builder = WebApplication.CreateBuilder(args);
//registe IOrderService service
builder.Services.AddScoped();
var app = builder.Build();
app.MapGet("/", () => "Hello World!");
//add order save API
app.MapPost("/add",(Order order,IOrderService service)=>
{
service.CreateOrder(order);
}).WithName("addorder");
//add order query API
app.MapGet("/getorder",(int id, IOrderService service)=>
{
return service.GetOrder(id);
}).WithName("getorder");
app.Run();
上面的低码中,首先增加一个文件级别的 namespace,这个地方为了和大家示意 Global Namespace 的区别
using NET6;
然后,在 ASP.NET DI 依赖注入框架中添加 IOrderService 服务:
//registe IOrderService service
builder.Services.AddScoped();
添加订单 Order 保存API 服务:
//add order save API
app.MapPost("/add",(Order order,IOrderService service)=>
{
service.CreateOrder(order);
}).WithName("addorder");
上面这个 HttpWebAPI,我们采用了 Post 方式,请求是必须传入 order 参数。
这个代码中,我们看到保存订单方法有2个参数,一个是 Order,另一个是 IOrderService,第二个参数,原生支持依赖注入,不需要显式声明创建。
类似的,继续添加查询订单 API 服务
//add order query API
app.MapGet("/getorder",(int id, IOrderService service)=>
{
return service.GetOrder(id);
}).WithName("getorder");
▌运行调试
在终端中输入 dotnet run 指令,启动运行调试
dotnet run
调试这3个 API,建议大家使用 PostMan 工具
先说一个小坑,一开始使用 PostMan 工具调试保存订单接口,将 order 显式地参数放到 Headers 中请求,结果一直不通:
看了微软的示例文档后,建议直接将 order json 对象,http 请求体中以 raw 的方式发起请求
其他的 API 接口则没有这个问题:
以上是.NET 6 Minimal APIs 的一些简单介绍和实践,希望能帮助大家。