• 通俗理解.NET 6 Minimal APIs


       .NET 6,微软称为“最快的.NET”,带有了许多令人兴奋的新功能、语言和性能改进。这是自 .NET Core 3.1 以来的第一个 LTS 版本,将支持三年。

       本次大版本发布,增加了一个新特性:Minimal APIs,这是什么技术?

            .NET6 使编写具有最小依赖性的 REST API 变得非常简单。

       乍一看,Minimal APIs 似乎是微软对 NodeJS(使用 ExpressJS)HTTP 服务器的回应,它提供了最小的 API。

       但是微软也对这项技术增加了几个关键词

    • LightWeight,Single file,Cloud Native API
    • Low ceremony,Top-Level C# programs
    • Easy to get started
    • Path to MVC

       总结一句话:.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 看看里面的内容,有一个配置:

           <ImplicitUsings>enable</ImplicitUsings>
     

     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设计和实现:

      3.1 先准备好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<Order> orders = new List<Order>();
    
            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);
            }
        }
    }

     3.2 在Program.cs类中,注册IOrderService服务,添加AddOrder和GetOrder Http Web API

      

    using NET6;
    
    var builder = WebApplication.CreateBuilder(args);
    
    //registe IOrderService service
    builder.Services.AddScoped<IOrderService, OrderServiceRepository>();
    
    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<IOrderService, OrderServiceRepository>();

       添加订单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");

     3.3 运行调试

      在终端中输入dotnet run指令,启动运行调试

    dotnet run

     调试这3个API,建议大家使用PostMan工具

     先说一个小坑,一开始使用PostMan工具调试保存订单接口,将order显式地参数放到Headers中请求,结果一直不通:

      

       看了微软的示例文档后,建议直接将order json对象,http请求体中以raw的方式发起请求

       

         其他的API接口则没有这个问题:

        

      好了,以上是.NET 6 Minimal APIs的一些简单介绍和实践,希望能帮助大家。

    周国庆

    2022/2/28

      

     
  • 相关阅读:
    软件工程 2016.6.28 日报
    软件工程课程总结
    工大助手--项目总结
    工大助手--加权平均分计算
    工大助手--数据查询
    7.5
    7月4日日报
    7.3日报
    6.30日报
    6.29.日报
  • 原文地址:https://www.cnblogs.com/tianqing/p/15941896.html
Copyright © 2020-2023  润新知