本报讯 柬埔寨王宫事务部9月24日发布通告称,最受柬埔寨民众尊敬、爱戴的诺罗敦·莫尼列·西哈努克王太后陛下已年过花甲,目前正在北京的医院接受悉心治疗。...
2025-09-25 8
在以前的MVC引用程序中,控制器是一个功能齐全的框架,但它偏重,因此在.Net6.0官方引入了MinimalAPIs,即最小API,与MVC相比,它足够的简洁,适合小型服务来使用,下面就让我们看看如何使用MinimalAPI来开发一个web应用程序
入门
下面我们来看一下官方提供的MinimalAPI是如何使用的
前提条件:安装 .NET 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()是如何来使用的
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上多了一个服务
细心的小伙伴发现了,这个服务好像是我们新增的添加用户服务,但链接地址为什么是api/v1/Users呢?让我们接着往下看。
进阶
通过快速入门我们了解到如何使用MinimalAPI,但我们也清楚流水账式编程的危害,我们不希望让项目中充斥着流水账式的代码,我们希望它是整洁的,并且是有迹可循的,这时候MASA提供的MinimalAPI方案进入了我们的视野,它上手难度极低,对我们来说它是很棒的,但如果我们不清楚它是如何设计的话,我们敢放心大胆的使用它吗?虽然它有些枯燥,但我们必须要掌握它是如何设计的,它都支持了什么样的功能
约定
当服务未禁用自动映射路由时,框架会自动扫描继承ServiceBase的非抽象子类并注册到服务集合中(IServiceCollection),并为满足以下要求的方法自动注册路由
当前类的方法的访问级别为public(不包含父类方法)
方法上未增加特性IgnoreRouteAttribute
当前类的方法的访问级别为public(不包含父类方法)
方法上未增加特性IgnoreRouteAttribute
路由规则优先级:
自定义路由 > 约定生成路由
如何自定义路由?
通过RoutePattern特性我们可以为方法自定义路由
[RoutePattern("user/add")]
public IResult Add([FromBody]RegisterUserRequest request)
//模拟添加用户
return Results.Ok;
约定的生成路由规则为:
Pattern(路由) = BaseUri + RouteMethodName
BaseUri: 根地址,默认: null
当BaseUri为空或者null时,则 BaseUri = Prefix/Version/ServiceName
RouteMethodName: 除非自定义RouteMethodName,否则RouteMethodName = GetMethodName(方法名)
BaseUri: 根地址,默认: null
当BaseUri为空或者null时,则 BaseUri = Prefix/Version/ServiceName
当BaseUri为空或者null时,则 BaseUri = Prefix/Version/ServiceName
RouteMethodName: 除非自定义RouteMethodName,否则RouteMethodName = GetMethodName(方法名)
TrimStart:Get/Post/Create/Put/Update/Delete/Remove等
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
Map
Assemblies: 用于扫描服务所在的程序集,默认: AppDomain.CurrentDomain.GetAssemblies
RouteHandlerBuilder: 基于RouteHandlerBuilder的委托,可用于权限认证、Cors等
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
Map
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
Map
BaseUri: 根地址,默认: null
ServiceName: 自定义服务名,默认: null
RouteHandlerBuilder:基于RouteHandlerBuilder的委托,可用于权限认证、Cors等
RouteOptions: 局部路由配置
DisableAutoMapRoute
Prefix
Version
AutoAppendId
PluralizeServiceName
GetPrefixes
PostPrefixes
PutPrefixes
DeletePrefixes
DisableTrimMethodPrefix
Map
DisableAutoMapRoute
Prefix
Version
AutoAppendId
PluralizeServiceName
GetPrefixes
PostPrefixes
PutPrefixes
DeletePrefixes
DisableTrimMethodPrefix
Map
其中ServiceName为null时,
ServiceName = 类名.TrimEnd("Service")//不区分大小写
其中ServiceName为null时,
ServiceName = 类名.TrimEnd("Service")//不区分大小写
用于自定义路由,支持参数
Pattern: 自定义路由或自定义方法名
当StartWithBaseUri:true,Pattern为自定义方法名
当StartWithBaseUri:false,Pattern为自定义路由
StartWithBaseUri: 是否基于BaseUri进行追加,默认: false
Pattern: 自定义路由或自定义方法名
当StartWithBaseUri:true,Pattern为自定义方法名
当StartWithBaseUri:false,Pattern为自定义路由
当StartWithBaseUri:true,Pattern为自定义方法名
当StartWithBaseUri:false,Pattern为自定义路由
StartWithBaseUri: 是否基于BaseUri进行追加,默认: false
用于忽略方法自动映射,例如;存在某个方法已经手动指定映射路由,不希望框架重复进行映射可使用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(")]
public void CreateUser([FromBody] RegisterUserRequest request, IData data)
data.Add(request.Name, request.Age);
如果你希望手动指定方法的请求类型,则可以使用[RoutePattern("/api/v2/user/add", ")]
如果你希望手动指定方法的请求类型,则可以使用[RoutePattern("/api/v2/user/add", ")]
场景四:
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将对当前服务失效,局部配置存在时,全局配置将不起作用
但是你必须知道的是,如果在服务内配置了RouteHandlerBuilder,那么全局配置的RouteHandlerBuilder将对当前服务失效,局部配置存在时,全局配置将不起作用
场景五:
Q: 我希望某个服务不需要经过授权即可访问,那我该怎么做?
A: 只需要在方 法上加AllowAnonymous特性即可, 它是MinimalAPI支持的,除了AllowAnonymous、EnableCors、Authorize等都是支持的, 但特性是不支持的
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的构造函数参数仅支持Singleton或Transient。如果您的服务的生命周期为Scoped,建议在对应的方法中增加参数,例如:
public void Add([FromBody] RegisterUserRequest request, IData data)
data.Add(request.Name, request.Age);
Q2:模型校验不起作用?
A:目前版本的MinimalAPI并不支持模型绑定与验证,后续版本()会增加支持
Q3:Builder.AddServices又为什么必须要放到最后?
A:我们知道通过builder.Build可以得到WebApplication,但在.Net6.0中新增加了限制,这个限制就是在Build后无法再次更新IServiceCollection,否则会提示Cannot modify ServiceCollection after application is built
Q4:为什么MinimalAPIs的生命周期是单例?
A:目前AddServices方法中做了两件事,第一件事就是获取到所有的服务,并注册到服务集合中,第二件事就是触发服务并将对应服务的地址以及方法映射到到App,App.Map类似App.Use,也是一个扩展方法,类似MVC的路由,其生命周期是单例,我们仅仅是将继承ServiceBase的服务映射到App中,并没有魔改MinimalAPI,因此并不存在性能问题,但同样其生命周期也无法改变
总结
MinimalAPI与MVC我应该如何选择?
小型服务使用MinimalAPI,因为它是很轻量级的,但如果是大型服务或者功能特别复杂的,还是推荐使用MVC,MinimalAPI的上手成本很低,但它不是银弹,选择适合自己的才是最好的
MinimalAPI还有一些特殊的地方,例如Get请求无法使用类对象来接收参数,如果希望使用类对象来接受,
则需要使用自定义绑定()
除此之外还有其他不一样的地方
完整文档可查看()
本章源码
Assignment14
开源地址
MASA.Framework:
如果你对我们的 MASA Framework 感兴趣,无论是代码贡献、使用、提 Issue,欢迎联系我们
《MASA Framework实战课程》已开课
相关文章
本报讯 柬埔寨王宫事务部9月24日发布通告称,最受柬埔寨民众尊敬、爱戴的诺罗敦·莫尼列·西哈努克王太后陛下已年过花甲,目前正在北京的医院接受悉心治疗。...
2025-09-25 8
自从特鲁多政府在美国的怂恿下,对中国产电动汽车、钢铁和铝产品加征高额关税以来,加拿大与中国的关系就如同滑下了拼命向下的轨道。中国这一举动无疑是给加拿大...
2025-09-25 7
据报道,近年来的中美博弈已不仅仅体现在贸易战、科技封锁和军事对抗等领域,金融和资本市场的较量同样成为了全球关注的焦点。中国连续大规模减持美国国债,特别...
2025-09-25 7
近日,四川成都一自行车车迷赛中发生选手集体摔车事故,一名年轻男选手疑似膝盖伤情严重,被传“膝盖骨没了”,引发众多网友热议。日前,成都市体育局回应新黄河...
2025-09-25 5
【环球网快讯】据《以色列时报》25日最新消息,以色列军方宣布,一名以色列国防军士兵在加沙城遭巴勒斯坦伊斯兰抵抗运动(哈马斯)狙击手袭击身亡。 报道称,...
2025-09-25 7
花费不菲入住五星级酒店 半夜却无奈躲在阳台 究竟发生了什么? 近日 30岁的孙先生反映 夫妻俩入住上海星河湾酒店后 自己眼角膜受伤,视物不清 妻子肺部...
2025-09-25 5
每经编辑|何小桃 据新黄河9月24日报道,记者近日获悉,浙江省绍兴市轨道交通2号线9月13日晚曾发生一起地铁列车撞上保洁员致多人伤亡的安全事故。 多名...
2025-09-25 5
资料图:上海绿捷 本文综合上观新闻、正观新闻、采招网、21世纪经济报道等 昨天(9月23日)深夜,针对公众关注的“臭味学生餐”事件,上海官方发布通报称...
2025-09-25 6
发表评论