[Proposal] Minimal APIs ServiceBase Refactor
假设标准服务写法
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("api/v1/catalog/{id}", GetAsync);
app.MapGet("api/v1/catalog/{id}/product", GetProductAsync);
app.MapPost("api/v1/catalog/{id}/product", CreateProductAsync);
app.MapPut("api/v1/catalog/{id}/product", UpdateProductAsync);
app.MapDelete("api/v1/catalog/{id}/product", DeleteProductAsync);
app.Map("api/v1/catalog/test", TestAsync);
app.Run();
public async Task<IResult> GetAsync(int id)
{
// do something
}
public async Task<IResult> GetProductAsync(int id)
{
// do something
}
public async Task<IResult> CreateProductAsync(int id, CreateProductDto product)
{
// do something
}
public async Task<IResult> UpdateProductAsync(int id, UpdateProductDto product)
{
// do something
}
public async Task<IResult> DeleteProductAsync(int id)
{
// do something
}
public async Task<IResult> TestAsync()
{
// do something
}
优化目标
自动解析ServiceBase,按照默认Route规则进行注册:
{Prefix}/{Version}/{DefaultTrimServiceName(ServiceName)}/{DefaultTrimMethod(method)}/{id?}
DefaultTrimServiceName
- TrimEnd:Service
DefaultTrimMethod:
- TrimStart:Get/Post/Create/Put/Update/Delete/Remove 等
- TrimEnd:Async
PS:如api/v1/user/GetAsync/1,将会变为 api/v1/user/1
优化过程
分离Map到独立的服务文件
program.cs
var builder = WebApplication.CreateBuilder(args);
var app = builder.Services.AddService(builder); //可能需要将builder,也有可能会去掉,要看MasaApp中保存Builder的顺序是否更早。
app.Run();
CatalogService.cs
public class CatalogService: ServiceBase
{
public CatalogService() // 去掉将IServiceCollection传递给基类
{
App.MapGet("api/v1/catalog/{id}", GetAsync);
App.MapGet("api/v1/catalog/{id}/product", GetProductAsync);
App.MapPost("api/v1/catalog/{id}/product", CreateProductAsync);
App.MapPut("api/v1/catalog/{id}/product", UpdateProductAsync);
App.MapDelete("api/v1/catalog/{id}/product", DeleteProductAsync);
App.Map("api/v1/catalog/test", TestAsync);
}
}
扫描非当前程序集的ServiceBase
program.cs
var builder = WebApplication.CreateBuilder(args);
var app = builder.Services.AddService(builder, options => { AdditionalAssemblies = new[] {} });
app.Run();
修改固定前缀
program.cs,全局设置,注意:所有参数为空则表示忽略,如Version = "",则Url将是api/{controller}/...
var builder = WebApplication.CreateBuilder(args);
var app = builder.Services.AddService(builder, options =>
{
Prefix = "api",
Version = "v1",
PluralizeServiceName = false // 设置为true则自动将service name变成复数,如 UserService -> api/v1/users
});
app.Run();
CatalogService.cs,单服务设置
public class CatalogService: ServiceBase
{
public CatalogService()
{
// 可有可无,设置后优先级将覆盖全局配置
RouteOptions.Prefix = "api";
RouteOptions.Version = "v2";
RouteOptions.PluralizeServiceName = true;
}
}
自动注册方法
program.cs,全局设置
var builder = WebApplication.CreateBuilder(args);
var app = builder.Services.AddService(builder, options =>
{
// *Prefixes 将会告诉自动扫描的方法,如何通过前缀将方法注册到对应的HttpMethod下
GetPrefixes = new string[] { "Get", "Select" } // 默认值是 Get
PostPrefixes = new string[] { "Upsert", "Create" } // 默认值是 Add, Create
PutPrefixes = new string[] { "TryPut", "Put" } // 默认值是 Update, Modify
DeletePrefixes = new string[] { "TryDelete" } // 默认值是 Delete/Remove
});
app.Run();
CatalogService.cs,单服务设置
public class CatalogService: ServiceBase
{
public CatalogService()
{
// 留空即可
// 1. 根据全局设置 Get, Post, Put, Delete 自动扫描方法注册
// 2. 自动TrimEnd("Async")
// 3. 未被识别的将通过Map的方式公开所有HttpMethod
}
}
个别破坏规则的需要单独指定
CatalogService.cs,单服务设置
public class CatalogService: ServiceBase
{
public CatalogService()
{
App.MapGet("v1-beta/demo/test", TestAsync); // 注册为HttpMethod = HttpGet,Url = "v1-beta/demo/test",直接通过App的Map系列方法将绕开所有的规则,直接注册到MinimalAPIs上
}
}
任务列表
可以通过 [x] 选择已完成
- [ ] 分离Map到独立的服务文件
- [ ] 扫描非当前程序集的ServiceBase
- [ ] 修改固定前缀
- [ ] 自动注册方法
- [ ] 个别破坏规则的需要单独指定
#238
此处为Route.Prefix = "api";
public class CatalogService: ServiceBase
{
public CatalogService()
{
// 可有可无,设置后优先级将覆盖全局配置
Route.Prefix = "api";
Route.Version = "v2";
Route.PluralizeServiceName = true;
}
}
按照默认Route规则进行注册:api/v1/{controller}/{id?}/{method}
这里如果参数有id,默认注册为api/v1/{controller}/{id}或api/v1/{controller}/{id?}
路由的规则多种多样,无法满足所有情况,如果需要的路由地址为api/v1/{controller}/{id?}/{method},则可以重写string GetMethodName(MethodInfo methodInfo)方法
已更新
In 0.6.0 this has been handled