入门
下面我们来看一下官方提供的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
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
中,这样岂不是变成流水账式的代码?那怎么做才能使得我们的代码更加美观呢?MinimalAPIs
(https://github.com/masastack/MASA.Framework/blob/main/src/Contrib/Service/Masa.Contrib.Service.MinimalAPIs/README.zh-CN.md)是如何来使用的Assignment.MinimalApiDemo
,并安装Masa.Contrib.Service.MinimalAPIs
dotnet add package Masa.Contrib.Service.MinimalAPIs --version 0.6.0-preview.13
MinimalAPI
,修改Program
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
路由规则
-
如何自定义路由?
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(方法名)
GetMethodName:
-
TrimStart: Get/Post/Create/Put/Update/Delete/Remove
等 -
TrimEnd: Async
/api/v1/User/Add
,将会变成/api/v1/User
/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(根据方法名前缀自动识别),如果希望指定请求类型而非自动识别,则可手动指定: Get
、Post
、Put
、Delete
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
提供了哪些配置,那下面就让我们实战来演练一下,通过模拟不同的场景使用不同的配置,以确保我们正确掌握这些知识MASA
提供的MinimalAPI
了,对新版的MinimalAPI
很喜欢,但我暂时不希望更改手动注册的方式,我希望升级之后不会对我现有的项目造成影响,我不希望将升级导致原来的服务无法访问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")]
Authorize
特性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
将对当前服务失效,局部配置存在时,全局配置将不起作用
A: 只需要在方法上加
AllowAnonymous
特性即可, 它是MinimalAPI支持的,除了AllowAnonymous
、EnableCors
、Authorize
等都是支持的, 但HttpGet
、HttpPost
、HttpPut
、HttpDelete
特性是不支持的
public class UserService : ServiceBase
{
[AllowAnonymous]
public void CreateUser([FromBody] RegisterUserRequest request, IData data)
{
data.Add(request.Name, request.Age);
}
}
常见问题
DbContext
已经被释放?Singleton
或Transient
。如果您的服务的生命周期为Scoped
,建议在对应的方法中增加参数,例如:
public void Add([FromBody] RegisterUserRequest request, IData data)
{
data.Add(request.Name, request.Age);
}
MinimalAPI
并不支持模型绑定与验证,后续版本(https://github.com/dotnet/aspnetcore/issues/39504)会增加支持Builder.AddServices()
又为什么必须要放到最后?builder.Build()
可以得到WebApplication
,但在.Net6.0中新增加了限制,这个限制就是在Build
后无法再次更新IServiceCollection
,否则会提示Cannot modify ServiceCollection after application is built
MinimalAPIs
的生命周期是单例?AddServices
方法中做了两件事,第一件事就是获取到所有的服务,并注册到服务集合中,第二件事就是触发服务并将对应服务的地址以及方法映射到到App
,App.Map
类似App.Use
,也是一个扩展方法,类似MVC的路由,其生命周期是单例,我们仅仅是将继承ServiceBase
的服务映射到App
中,并没有魔改MinimalAPI
,因此并不存在性能问题,但同样其生命周期也无法改变总结
MinimalAPI
与MVC
我应该如何选择?MinimalAPI
,因为它是很轻量级的,但如果是大型服务或者功能特别复杂的,还是推荐使用MVC
,MinimalAPI
的上手成本很低,但它不是银弹,选择适合自己的才是最好的Get
请求无法使用类对象来接收参数,如果希望使用类对象来接受,本章源码
Assignment14
https://github.com/zhenlei520/MasaFramework.Practice
开源地址
MASA.Framework:https://github.com/masastack/MASA.Framework