• 使用 FastEndpoints 来垂直切割Web API的控制器方法


    在我们开发项目的Web API的时候,随着项目功能要求越来越多,可能我们会为控制器基类增加越来越多的基础功能,有些功能有一定的适应性,但可能在一般的子类中用不到,而随着对控制器控制要求越来越精细,那么需要为基类或者子类增加更多的控制功能,这样随着迭代的进行,有些控制器的功能会显得越来越笨重。这个时候,一种更加灵活、轻便的Web API处理方式,对每个控制器方法的垂直切割的API框架应运而生,本篇随笔介绍的FastEndpoints 就是其中这样的一款框架,本篇随笔介绍一些FastEndpoints的基础处理方法,并通过一些基础的案例,把我们《 SqlSugar 开发框架》的一些模块进行迁移性测试,对比相关后端Web API的处理,一起分享给大家。

    1、FastEndpoints介绍

    FastEndpoints 是Minimal API和MVC的开发人员友好替代品,它是基于REPR设计模式(请求-端点-响应),以便创建方便且可维护的端点,几乎没有样板文件。

    FastEndpoints 的性能与Minimal API 相当,甚至它更快,使用更少的内存并且每秒请求数比基准测试中的MVC控制器更高。对于比如:中间件、认证、授权、日志,依赖注入这些常用功能都支持,甚至有些还进行了加强。

    设计主要是分为两种模式

    分层模式:mvc、mvp、mvvm等
    垂直模式:REPR设计模式
    REPR设计模式就是垂直模式,系统的每个组件都是单独的一块,彼此并不影响,就像微服务那样。

    MVC - 模型-视图-控制器旨在与用户界面配合使用。显然,视图是一个 UI 组件。如果您正在构建 API,则没有视图,因此您充其量使用的是 MC 模式,或者您可以将其称为模型-操作-控制器并获取 MAC 模式。关键是,你已经没有将MVC用于你的API,所以考虑一个更合适的模式应该不是一个很大的问题。

    API 端点是非常独立的,每个端点都可以使用三个组件来描述:

    请求(Request):终结点所需的数据形状
    终结点(Endpoint):终结点在给定请求时执行的逻辑
    响应(Response):终结点返回给调用方的响应
    结合这三个元素,你会得到请求-端点-响应或 REPR 模式。

    并非所有终结点都需要其请求或响应的实际数据,在某些情况下,不接收任何输入或仅返回 HTTP 状态代码。但是,在此模式中,空请求或响应仍然是有效的请求或响应,就像某些 MVC 操作不需要模型一样。

    使用 API 端点库时,您可以将请求、终端节点和响应类型分组在一起,这样就无需在某些“视图模型”或“dtos”文件夹中四处寻找合适的类型。它减少了摩擦,使使用单个端点变得更加容易。

    FastEndPoint GitHub库:https://github.com/FastEndpoints/FastEndpoints

    FastEndpoints 在线文档:https://fast-endpoints.com 

    2、简单例子入门

    参考官方的文档介绍,我们可以很容易的创建出一个简单的类似Hello开篇的API应用。

    我创建一个基于.net core的Web API项目,先把FastEndPoint的相关引用加入项目中,如下所示。

     然后在项目中的启动类代码中,我们添加相关的代码使用FastEndpoints,如下所示。

    复制代码
    using FastEndpoints;
    using FastEndpoints.Swagger;
    
    var bld = WebApplication.CreateBuilder();
    bld.Services
       .AddFastEndpoints()
       .SwaggerDocument(); 
    
    var app = bld.Build();
    app.UseFastEndpoints()
       .UseSwaggerGen(); 
    app.Run();
    复制代码

    如果需要对Swagger进行一些定制修改,可以改动如下,这里先忽略。

    复制代码
        .SwaggerDocument(o =>
        {
            o.DocumentSettings = s =>
            {
                s.Title = "SqlSugar框架接口API文档";
                s.Version = "v1";
            };
            o.TagDescriptions = t =>
            {
                t["Test"] = "测试接口";
                t["User"] = "用户相关接口";
            };
        })
    复制代码

    为了简便,我们以命名控件不同,以及目录,来区分不同的Web API分组,如下所示,我们创建一个基于Test的相关API接口。

    对于以前的控制器接口来说,一般可能一个控制(如TestController)会包含多个方法,如上面的Create、List方法,这里使用的是FastEndpoints,它们是把一个大型的控制器切换为一个方法一个类来处理,碎片化意味着类的增加,不过我们不需要做太多的工作,可以通过它们的一些基类来简化这个过程。

    我们把WebAPI中请求的Request和Response的对象,放在一个Model类文件里面,如下代码所示。

    复制代码
    namespace FastWebApi.Controllers.Test
    {
        /// 
        /// 测试请求信息
        /// 
        public class TestRequest
        {
            public string FirstName { get; set; }
            public string LastName { get; set; }
            public int Age { get; set; }
        }
    
        /// 
        /// 测试返回信息
        /// 
        public class TestResponse
        {
            public string FullName { get; set; }
            public bool IsOver18 { get; set; }
        }
    }
    复制代码

    我们来看看基于FastEndPoints方式 生成一个Create的请求Web API方法,如下代码所示

    复制代码
    namespace FastWebApi.Controllers.Test
    {
        //客户使用标识,不用覆盖 Configure 函数
        //[HttpPost("/api/user/create")]
        //[AllowAnonymous]
    
        /// 
        /// 创建记录
        /// 
        public class Create : Endpoint
        {
            public override void Configure()
            {
                Post("/test/create");
                AllowAnonymous();
            }
    
            public override async Task HandleAsync(TestRequest req, CancellationToken ct)
            {
                var result = new TestResponse()
                {
                    FullName = req.FirstName + " " + req.LastName,
                    IsOver18 = req.Age > 18
                };
                await SendAsync(result.ToAjaxResponse());
            }
        }
    }
    复制代码

    我们配置Web API方法的路由,可以通过在Configure函数中指定: Post("/test/create")

    也可以通过Attribute属性标识的方式,来声明,上面的注释代码所示。

    [HttpPost("/api/user/create")]

    这两者是等同的,任何一种方式都可以,默认的接口是需要授权才能访问的,如果我们标识了

    [AllowAnonymous]

    就可以匿名访问Web API 的方法了,Web API的方法处理逻辑,都是统一通过重写 HandleAsync 方法进行实现的,如上面代码所示。

    其中AjaxResponse 是我定义的一个统一返回结果,这样我们的接口模型就一致了。

    如下是Web API统一封装后返回的结果对象。

    如果需要了解我的《SqlSugar开发框架》的统一结果返回处理,可以参考《基于SqlSugar的数据库访问处理的封装,在.net6框架的Web API上开发应用 》中的 【统一结果封装和异常处理】 部分内容即可。

    如果不需要统一返回模型,则可以自定义为任何的返回类型,如下是官方的案例所示。

    复制代码
    public class MyEndpoint : EndpointMyResponse>
    {
        public override void Configure()
        {
            Post("/api/user/create");
            AllowAnonymous();
        }
    
        public override async Task HandleAsync(MyRequest req, CancellationToken ct)
        {
            await SendAsync(new()
            {
                FullName = req.FirstName + " " + req.LastName,
                IsOver18 = req.Age > 18
            });
        }
    }
    复制代码

    接下来,我们检查下.netcore项目的launchSettings.json 配置信息

    确保打开的时候就启动Swagger页面即可。

    启动Swagger页面,我们来看看具体的效果,可以看到有两个Test的接口,如下所示。

    我们来调试Swagger,并测试下结果返回。

    测试返回的结果如下所示,由于采用了统一返回结果的处理,这里返回的TestResponse的对象序列化信息,放在了result的里面了,如下所示。

    而List的控制器方法,这里没有请求输入的对象信息,因此参数为空。具体的API方法定义如下所示。

    复制代码
    namespace FastWebApi.Controllers.Test
    {
        /// 
        /// 获取所有记录
        /// 
        [HttpGet("/test/list")]
        [AllowAnonymous]
        public class List : EndpointWithoutRequest<AjaxResponse>
        {
            /// 
            /// 处理返回
            /// 
            public override async Task HandleAsync(CancellationToken ct)
            {
                var result = new List()
                {
                    new TestResponse
                    {
                         FullName= "test",
                          IsOver18 = true,
                    },
                    new TestResponse
                    {
                        FullName= "test 2",
                          IsOver18 = false,
                    }
                };
               await SendAsync(result.ToAjaxResponse());
            }
        }
    }
    复制代码

    Swagger接口展示界面效果。

    正常执行返回结果如下所示。

     如果处理过程中有异常,由于我们采用了统一返回结果处理,因此异常信息也需要统一在对象里面,返回结果如下所示。

     以上就是简单类型的一些处理例子,结合了统一返回结果的处理,我们可以很好的定义一个通用的结果返回。

     

    3、对我们SqlSugar框架常规CRUD等基类接口进行垂直切割的处理

    上面我们为了更好理解FastEndpoints的碎片化接口的处理,我们做了两个简单的方法来测试。

    下面我们通过对我们《SqlSugar开发框架》中的基类接口进行功能上的拆分,并结合实际业务的需要接口,进行扩展的处理,从而也实现了常规CRUD的操作接口,并实现特殊业务类的API接口处理。

    关于Web API的常规接口处理 ,我们为了简化代码,往往抽象一些常规的CRUD方法在控制器基类中,这样可以极大的减少了继承子类的接口代码,通过继承基类,子类自动具备了CRUD的处理接口,只需要根据业务的需要,增加一些特殊的业务接口即可。

    以前的处理方法,我们是根据项目的需要,我们定义了一些控制器的基类,用于实现不同的功能,如下界面所示。

    其中ControllerBase是.net core Web API中的标准控制器基类,我们由此派生一个LoginController用于登录授权,而BaseApiController则处理常规接口用户身份信息,而BusinessController则是对标准的增删改查等基础接口进行的封装,我们实际开发的时候,只需要开发编写类似CustomerController基类即可。

    而现在采用FastEndpoints ,需要垂直切割整个控制器,这种架构称为 “垂直切片架构", 系统的每个组件都是单独的一块,彼此并不影响,就像微服务那样。

    我们需要把基类的接口实现放到具体的业务API类里面,为了方便,可以给它们不同的名称一个接口,或者组合在一个文件里面,如下所示。

    我们来看看其中给一个简单的Count方法接口实现。

    复制代码
    namespace FastWebApi.Controllers.User
    {
        /// 
        /// 根据条件计算记录数量
        /// 
        [HttpGet("/user/count")]
        public class Count : Endpoint
        {
            /// 
            /// 处理请求
            /// 
            public override async Task HandleAsync(UserPagedDto req, CancellationToken ct)
            {
                var result = await BLLFactory.Instance.CountAsync(req);
                await SendAsync(result.ToAjaxResponse());
            }
        }
    }
    复制代码

    这里可以采用接口注入的方式,也可以采用我们辅助类BLLFactory.Instance方法调用接口,一样的实现。

    这样结合了业务的具体Service来处理,只需要简单的处理下即可,也算比较方便,由于这些基础的CRUD的方法,主要路由、分页对象,业务对象,主键类型的不同,这些可以通过我们的代码生成工具的处理快速生成即可,因此可以实现批量化的业务类的API接口方法生成。

    至于具体的业务接口API,我们就需要手工处理了,如对于用户的登陆获取token的方法,我们这里需要模仿来生成一个EndPiont类,如下所示。

    复制代码
        /// 
        /// 根据用户名、密码验证用户身份有效性
        /// 
        [HttpPost("/user/verify-user")]
        [AllowAnonymous]
        public class VerifyUser : Endpoint
        {
            /// 
            /// 处理请求
            /// 
            public override async Task HandleAsync(VerifyUserDto input, CancellationToken ct)
            {
                var result = await BLLFactory.Instance.VerifyUser(input.UserName, input.UserPassword, input.SystemType, input.IP, input.MacAddr);
                await SendAsync(result.ToAjaxResponse());
            }
        }
    复制代码

    其他业务方法也是类似的处理,这里的FastEndPoints的处理类,只是增加了一个简单的包装层就可以了,最后看看这些方法在SwaggerUI中的展示,和我们普通模式的Web API中的Swagger UI界面类似的效果。

     这样,我们可以在保持接口一致性的情况下,无缝的对接新的Web API接口后端了。

  • 相关阅读:
    AJAX学习(四)
    聊聊自动化测试想法
    如何做一个基于JAVA的新闻管理系统毕业设计毕设作品(springboot框架)
    KMP算法
    汽车行业深度报告:域控制器,汽车电子电气架构演进下的黄金赛道
    FFMpeg AVBufferPool 的理解与掌握
    SpringBoot项目实现日志打印SQL明细(包括SQL语句和参数)几种方式
    操作系统——信号量机制(王道视频p30、课本ch6)
    综合工具-Design Compiler使用(从RTL到综合出各种报告timing\area\critical_path)
    ssm+vue+elementUI基于微信小程序的电动电动汽车车智能充电桩服务平台-计算机毕业设计
  • 原文地址:https://www.cnblogs.com/wuhuacong/p/17903114.html