MASA Framework的MinimalAPIs应用

在以前的MVC引用程序中,控制器是一个功能齐全的框架,但它偏重,因此在.Net6.0官方引入了MinimalAPIs,即最小API,与MVC相比,它足够的简洁,适合小型服务来使用,下面就让我们看看如何使用MinimalAPI来开发一个web应用程序

入门

下面我们来看一下官方提供的MinimalAPI是如何使用的

  • 前提条件:安装.NET 6.0 (https://dotnet.microsoft.com/zh-cn/download/dotnet/6.0)

1.新建ASP.NET Core 空项目Assignment.MinimalApiDemo

dotnet new web -o Assignment.MinimalApiDemo
cd Assignment.MinimalApiDemo

2.增加一个Get请求,修改Program

app.MapGet("/test", () => "Test Success!");

根据需求,自行增加Get (MapGet)、Post (MapPost)、Put (MapPut)、Delete (MapDelete)方法即可,完整代码如下:

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("/test", () => "Test Success!");

MASA Framework版MinimalAPI

随着我们的服务变得越来越多,这些服务全部被堆积在Program中,这样岂不是变成流水账式的代码?那怎么做才能使得我们的代码更加美观呢?

下面我们就来看一下MASA Framework提供的MinimalAPIs (https://github.com/masastack/MASA.Framework/blob/main/src/Contrib/Service/Masa.Contrib.Service.MinimalAPIs/README.zh-CN.md)是如何来使用的

1.选中项目Assignment.MinimalApiDemo,并安装Masa.Contrib.Service.MinimalAPIs

dotnet add package Masa.Contrib.Service.MinimalAPIs --version 0.6.0-preview.13

2.注册MASA Framework版的MinimalAPI,修改Program

var app = builder.AddServices();

3.新增加一个用户的服务,新增UserService

public class UserService : ServiceBase
{public IResult Add(RegisterUserRequest request){//模拟添加用户return Results.Ok();}
}

如何新建类目到这里已经结束了,可能会有小伙伴十分的疑惑,MASA Framework提供的方案让我有点摸不着头脑,但项目运行后就会发现在Swagger上多了一个服务

cb338ecfc004f6bc8e3002e4c859c123.png

细心的小伙伴发现了,这个服务好像是我们新增的添加用户服务,但链接地址为什么是api/v1/Users 呢?让我们接着往下看。

进阶

通过快速入门我们了解到如何使用MinimalAPI,但我们也清楚流水账式编程的危害,我们不希望让项目中充斥着流水账式的代码,我们希望它是整洁的,并且是有迹可循的,这时候MASA提供的MinimalAPI方案进入了我们的视野,它上手难度极低,对我们来说它是很棒的,但如果我们不清楚它是如何设计的话,我们敢放心大胆的使用它吗?虽然它有些枯燥,但我们必须要掌握它是如何设计的,它都支持了什么样的功能

约定

当服务未禁用自动映射路由时,框架会自动扫描继承ServiceBase的非抽象子类并注册到服务集合中(IServiceCollection),并为满足以下要求的方法自动注册路由

  • 当前类的方法的访问级别为public(不包含父类方法)

  • 方法上未增加特性IgnoreRouteAttribute

路由规则

路由规则优先级:

自定义路由 > 约定生成路由

  1. 如何自定义路由?

通过RoutePattern特性我们可以为方法自定义路由

[RoutePattern("user/add")]
public IResult Add([FromBody]RegisterUserRequest request)
{//模拟添加用户return Results.Ok();
}
  1. 约定的生成路由规则为:

Pattern(路由) = BaseUri + RouteMethodName

  • BaseUri: 根地址,默认: null

    • BaseUri或者null时,则 BaseUri = Prefix/Version/ServiceName

  • RouteMethodName: 除非自定义RouteMethodName,否则RouteMethodName = GetMethodName(方法名)

GetMethodName:

  1. TrimStart:Get/Post/Create/Put/Update/Delete/Remove 等

  2. TrimEnd:Async

PS:/api/v1/User/Add,将会变成/api/v1/User

当方法的参数存在id并且id支持从Route中获取时,将会变成/api/v1/User/{id},如果id为可空或者存在默认值时,将会变成/api/v1/User/{id?}

配置

配置分为全局配置、局部配置(仅在当前服务生效),其中优先级为:局部配置 > 全局配置,默认局部配置的参数为null,我们约定局部参数未配置时,以全局配置为准

全局配置

  • DisableAutoMapRoute: 是否禁用自动映射路由,如果为true (禁用),则框架不会自动映射路由,默认:false

  • Prefix: 前缀,默认: api

  • Version: 版本,默认: v1

  • AutoAppendId: 是否追加Id,默认: true

  • PluralizeServiceName: 服务名称是否启用复数,默认: true

  • GetPrefixes: 用于识别当前方法类型为Get请求,默认: new List<string> { "Get", "Select" }

  • PostPrefixes: 用于识别当前方法类型为Post请求,默认: new List<string> { "Post", "Add", "Upsert", "Create" }

  • PutPrefixes: 用于识别当前方法类型为Put请求,默认: new List<string> { "Put", "Update", "Modify" }

  • DeletePrefixes: 用于识别当前方法类型为Delete请求,默认: new List<string> { "Delete", "Remove" }

  • DisableTrimMethodPrefix: 禁用移除方法前缀(Get/Post/Create/Put/Update/Delete/Remove 等), 默认: false

  • MapHttpMethodsForUnmatched: 匹配请求方式失败使用,默认: 支持Post、Get、Delete、Put

  • Assemblies: 用于扫描服务所在的程序集,默认: AppDomain.CurrentDomain.GetAssemblies()

  • RouteHandlerBuilder: 基于RouteHandlerBuilder的委托,可用于权限认证、Cors等

局部配置

  • BaseUri: 根地址,默认: null

  • ServiceName: 自定义服务名,默认: null

  • RouteHandlerBuilder:基于RouteHandlerBuilder的委托,可用于权限认证、Cors等

  • RouteOptions: 局部路由配置

    • DisableAutoMapRoute

    • Prefix

    • Version

    • AutoAppendId

    • PluralizeServiceName

    • GetPrefixes

    • PostPrefixes

    • PutPrefixes

    • DeletePrefixes

    • DisableTrimMethodPrefix

    • MapHttpMethodsForUnmatched

其中ServiceName为null时,

ServiceName = 类名.TrimEnd("Service") //不区分大小写

特性

RoutePattern

用于自定义路由,支持参数

  • Pattern: 自定义路由或自定义方法名

    • 当StartWithBaseUri:true,Pattern为自定义方法名

    • 当StartWithBaseUri:false,Pattern为自定义路由

  • StartWithBaseUri: 是否基于BaseUri进行追加,默认: false

  • HttpMethod:请求类型,默认: null(根据方法名前缀自动识别),如果希望指定请求类型而非自动识别,则可手动指定:GetPostPutDelete

IgnoreRoute

用于忽略方法自动映射,例如;存在某个方法已经手动指定映射路由,不希望框架重复进行映射可使用IgnoreRoute, 例如:

public class User2Service : ServiceBase
{public User2Service(){App.Map("/api/v2/user/add", Add);}[IgnoreRoute]public void Add([FromBody] RegisterUserRequest request, IData data){data.Add(request.Name, request.Age);}
}

场景

通过上面的学习我们已经了解到了MASA提供了哪些配置,那下面就让我们实战来演练一下,通过模拟不同的场景使用不同的配置,以确保我们正确掌握这些知识

场景一:

Q: 我不是一个新手,从0.6.0版本以前的版本就开始使用MASA提供的MinimalAPI了,对新版的MinimalAPI很喜欢,但我暂时不希望更改手动注册的方式,我希望升级之后不会对我现有的项目造成影响,我不希望将升级导致原来的服务无法访问

A: 你希望继续使用最新版的MinimalAPI,但不希望对原来的项目造成影响,在当前服务中,希望能一如既往的使用手动注册,而不是自动注册,那你可以配置全局禁用自动注册,例如:

var app = builder.AddServices(options =>
{options.DisableAutoMapRoute = true;
});

当然如果你希望在某个特定的服务中开启自动映射,则可以在服务中配置:

public class UserService: ServiceBase
{public UserService(){RouteOptions.DisableAutoMapRoute = false;}public void Add([FromBody] RegisterUserRequest request, IData data){data.Add(request.Name, request.Age);}
}

场景二:

Q: 我是一个新手,我觉得我的项目不需要使用前缀以及版本,我希望自动映射的路由可以帮助我删掉它们

A: 你需要的是全局配置,通过全局配置禁用前缀以及版本即可,例如:

var app = builder.AddServices(options =>
{options.Prefix = string.Empty;options.Version = string.Empty;
});

场景三:

Q: 我是一个新手,虽然我很想严格遵守Resetful标准来写服务,但遗憾的是我无法掌控全局,总是有人不按照标准对方法进行命名,我希望可以人为控制特定的方法的路由

A: 目前有两种方法可供选择,它们分别是:

第一种:自定义路由并忽略自动映射

public class UserService : ServiceBase
{public UserService(){App.Map("/user/add", Add);}[IgnoreRoute]public void Add([FromBody] RegisterUserRequest request, IData data){data.Add(request.Name, request.Age);}
}

第二种: 完整自定义路由

public class UserService : ServiceBase
{[RoutePattern("/api/v2/user/add")]public void CreateUser([FromBody] RegisterUserRequest request, IData data){data.Add(request.Name, request.Age);}
}

第三种: 仅修改请求方式

public class UserService : ServiceBase
{[RoutePattern(HttpMethod = "Post")]public void CreateUser([FromBody] RegisterUserRequest request, IData data){data.Add(request.Name, request.Age);}
}

如果你希望手动指定方法的请求类型,则可以使用[RoutePattern("/api/v2/user/add", HttpMethod = "Post")]

场景四:

Q: 我希望为项目中所有的接口都必须授权才能访问,但我不希望在每个方法上增加Authorize特性

A: 你的项目是需要为全局服务来设置,则可通过全局配置的RouteHandlerBuilder参数来完成,例如:

var app = builder.AddServices(options =>
{options.RouteHandlerBuilder = routeHandlerBuilder => routeHandlerBuilder.RequireAuthorization();
});

如果你希望对某个服务增加特殊的授权策略,则可以:

public class UserService : ServiceBase
{public UserService(){RouteHandlerBuilder = routeHandlerBuilder => routeHandlerBuilder.RequireAuthorization("test");}public void CreateUser([FromBody] RegisterUserRequest request, IData data){data.Add(request.Name, request.Age);}
}

但是你必须知道的是,如果在服务内配置了RouteHandlerBuilder,那么全局配置的RouteHandlerBuilder将对当前服务失效,局部配置存在时,全局配置将不起作用

场景五:

Q: 我希望某个服务不需要经过授权即可访问,那我该怎么做?
A: 只需要在方法上加AllowAnonymous特性即可, 它是MinimalAPI支持的,除了AllowAnonymousEnableCorsAuthorize等都是支持的, 但HttpGetHttpPostHttpPutHttpDelete特性是不支持的

public class UserService : ServiceBase
{[AllowAnonymous]public void CreateUser([FromBody] RegisterUserRequest request, IData data){data.Add(request.Name, request.Age);}
}

常见问题

Q1:为何使用DbContext时总是提示DbContext已经被释放?

A:UserService仅在项目启动时会被初始化一次,之后不再初始化,因此Service的构造函数参数仅支持SingletonTransient。如果您的服务的生命周期为Scoped,建议在对应的方法中增加参数,例如:

public void Add([FromBody] RegisterUserRequest request, IData data)
{data.Add(request.Name, request.Age);
}

Q2:模型校验不起作用?

A:目前版本的MinimalAPI并不支持模型绑定与验证,后续版本(https://github.com/dotnet/aspnetcore/issues/39504)会增加支持

Q3:Builder.AddServices()又为什么必须要放到最后?

A:我们知道通过builder.Build()可以得到WebApplication,但在.Net6.0中新增加了限制,这个限制就是在Build后无法再次更新IServiceCollection,否则会提示Cannot modify ServiceCollection after application is built

Q4:为什么MinimalAPIs的生命周期是单例?

A:目前AddServices方法中做了两件事,第一件事就是获取到所有的服务,并注册到服务集合中,第二件事就是触发服务并将对应服务的地址以及方法映射到到AppApp.Map类似App.Use,也是一个扩展方法,类似MVC的路由,其生命周期是单例,我们仅仅是将继承ServiceBase的服务映射到App中,并没有魔改MinimalAPI,因此并不存在性能问题,但同样其生命周期也无法改变

总结

MinimalAPIMVC我应该如何选择?

小型服务使用MinimalAPI,因为它是很轻量级的,但如果是大型服务或者功能特别复杂的,还是推荐使用MVCMinimalAPI的上手成本很低,但它不是银弹,选择适合自己的才是最好的

MinimalAPI还有一些特殊的地方,例如Get请求无法使用类对象来接收参数,如果希望使用类对象来接受,

则需要使用自定义绑定  (https://docs.microsoft.com/zh-cn/aspnet/core/fundamentals/minimal-apis?view=aspnetcore-6.0#custom-binding)

除此之外还有其他不一样的地方

完整文档可查看(https://docs.microsoft.com/zh-cn/aspnet/core/fundamentals/minimal-apis?view=aspnetcore-6.0)

本章源码

Assignment14

https://github.com/zhenlei520/MasaFramework.Practice

开源地址

MASA.Framework:https://github.com/masastack/MASA.Framework


如果你对我们的 MASA Framework 感兴趣,无论是代码贡献、使用、提 Issue,欢迎联系我们

4b43b72ac6b694e93c6b9c85b444b02f.png

44373e92bee8eac49fe35bad72974456.gif

《MASA Framework实战课程》已开课

点击“阅读原文”查看课程安排

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/282480.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

CrossPHP--在我们用ajax,js取不到指定数据时,我们可以换一种方式

项目中遇到的问题: 需求: 用的是layui的laypage组件,进行分页操作,熟悉layui的朋友都知道,laypage需要从服务端给其一个总条数, 但是在进行ajax请求时出了问题, 我是这样定义的但是调用的时候却无法将数值直接返回回去,所以这里只能更换一种思路 在控制器中进行数据的查询,然后…

MySQL设置从库只读模式

常见现象 运维工作中会经常维护MySQL主从服务器&#xff0c;当然Slave我们只是用于读操作。 一般权限开通也只授权只读账号&#xff0c;但是有时候维护工作可能不是一个人在做&#xff0c;你不能保证其他同事都按照这个标准操作。 有同事可能会授权Slave库MySQL账号为all或者se…

layui弹出层使用(layer.alert / layer.open / layer.prompt )

一 layer.alert 效果图: 代码: //取消提现 function back(id) {layer.alert(真的要取消吗, {skin: layui-layer-molv //样式类名 自定义样式,closeBtn: 1 // 是否显示关闭按钮,anim: 1 //动画类型,btn: [确定,取消] //按钮,icon: 6 // icon,yes:function(){return $.aj…

SkiaSharp 自绘弹幕效果

SkiaSharp 自绘弹幕效果控件名&#xff1a;SkiaSharpBarrage作者&#xff1a; 驚鏵原文链接&#xff1a; https://github.com/yanjinhuagood/SkiaSharpBarrage框架使用.NET60&#xff1b;Visual Studio 2022;项目使用 MIT 开源许可协议&#xff1b;接着上一篇 WPF 弹幕上期有…

JavaScript中this指向

一.重点来了&#xff0c;this指向问题&#xff1a;1.this指向之普通函数。 2.this指向之对象 3.this指向之构造函数 4.this指向之&#xff08;call,apply&#xff09;动态更改this指向。 二.具体分析如下 1.普通函数 // 第23行的调用者为null,this指向也为null,// 所以这时js把…

提交Form表单,submit之前做js判断处理

效果:在点击提交按钮时,首先进行js判断, 如果不符合条件,则alert出提示信息,并return false. 主要点就在于给form表单添加一个onsubmit事件. 在onsubmit事件中定义的函数里进行js验证处理.代码 : <!DOCTYPE html> <html lang"en"> <head><meta …

如何在Windows上一键部署PaddleOCR的WebAPI服务

PaddleOCR旨在打造一套丰富、领先、且实用的OCR工具库&#xff0c;助力开发者训练出更好的模型&#xff0c;并应用落地。官方开源项目地址&#xff1a;https://github.com/PaddlePaddle/PaddleOCR一定会有小伙伴们看完不知道如何部署与应用&#xff0c;怎么才能融入到自己的产品…

微软为 Visual Studio 扩展添加对 Arm64 的支持

微软在 6 月份推出了支持 Arm64 架构的 Visual Studio&#xff0c;这是第一个原生支持在基于 Arm 的处理器上构建和调试 Arm64 应用程序的 Visual Studio 版本。近日&#xff0c;他们宣布为 Visual Studio 扩展也添加了对 Arm64 的支持&#xff0c;因此开发者可在 Arm64 Visual…

WIN10 查看已经连接的wifi的密码

命令行: 1. 显示以前连接过的wifi2. 将wifi配置存入文件中3. 查看刚刚保存的wifi配置的文件这样,我们就可以看到连接的wifi名称和wifi密码了.

微软与 Canonical 合作,将 systemd 引入 WSL

微软和 Canonical 联合宣布&#xff0c;systemd 现在可以在 Windows Subsystem for Linux&#xff08;WSL2&#xff09;中运行了&#xff0c;此举可以让用户在 Windows 设备上获得更加全面的 Linux 体验。systemd 的作者 Lennart Poettering 在 7 月份离开红帽并加入了微软&…

.NET 反向代理-YARP 根据域名转发

编者&#xff1a;fastgithub 就是基于YARP使用域名做转发逻辑的。 前段时间发布过一个关于 YARP 的简单介绍&#xff0c;感兴趣的小伙伴恭请移步看看 .NET 反向代理-YARP 作为反向代理&#xff0c;必不可少的当然是根据域名代理转发啦&#xff0c;毫无疑问&#xff0c;YARP…

第一个python小游戏

guess int(input("猜一猜宝宝心目中的数字是多少:")) secret 8 while guess !secret:guess int(input("哎呀猜错了,重新猜一猜宝宝心目中的数字是多少:"))if guess secret:print("你真厉害,居然猜对了")print("哼,猜对了也不给你奖励&q…

理论实践:循序渐进理解AWR细致入微分析性能报告

1. AWR 概述 Automatic Workload Repository(AWR) 是10g引入的一个重要组件。在里面存贮着近期一段时间内&#xff08;默认是7天&#xff09;数据库活动状态的详细信息。 AWR 报告是对 AWR 视图进行查询而得到的一份自动生成的报告。可以通过下面的脚本手工得到一份 AWR 报告。…

mysql sql语句书写之面试部分

要求一 :查询时,将用户的手机号码(比如1331234567)显示为133***4567 这是在交流群里看到别人发的一个面试题,我本人非常反感直接在查询时进行处理数据的,查询出来再处理不好吗,但是面试题要求是这样. 这里,简单的写了两个表关联查询,然后把手机号码进行处理显示出来select a.ui…

Redis --数据类型 [1]

一 string 类型 (最简单常用的类型) string是redis最基本的类型&#xff0c;你可以理解成与Memcached一模一样的类型&#xff0c;一个key对应一个value。二 Hash类型(哈希) Redis hash是一个string类型的field和value的映射表&#xff0c;hash特别适合用于存储对象。三 List(列…

KestrelServer详解[3]: 自定义一个迷你版的KestrelServer

和所有的服务器一样&#xff0c;KestrelServer最终需要解决的是网络传输的问题。在《KestrelServer详解[2]: 网络连接是如何创建的&#xff1f;》&#xff0c;我们介绍了KestrelServer如何利用连接接听器的建立网络连接&#xff0c;并再次基础上演示了如何直接利用建立的连接接…

c# 文件下载

这样的下载方式 减少服务器的压力&#xff0c; 还有一种省懒劲的方式&#xff1a;后端在iis上配置一个虚拟目录&#xff0c;然后让前端自己拼url地址下载&#xff0c; 这个东西是给后期其他工作人员埋坑&#xff0c;哈哈。 本帖原文转自与 农码一生转载于:https://www.cnbl…

Redis -- 基础操作 [2]

一 获取redis当前数据库符合条件键名 [keys pattern]二 设置string形式key-value [set key value]三 获取存储在指定 key 中字符串的子字符串 [GETRANGE KEY start end]四 删除指定键值对 [del key]五 为给定key设置过期时间 [Expire KEY SECONDS]注: Expireat KEY TIMESTAMP 同…

Centos7作为VNCserver,本地使用VNCViewer连接

1.概念 VNC是一个远程连接工具 VNC is used to display an X windows session running on another computer. Unlike a remote X connection, the xserver is running on the remote computer, not on your local workstation. Your workstation ( Linux or Windows ) is only …