Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Proposal] Minimal APIs ServiceBase Refactor #241

Closed
5 tasks
doddgu opened this issue Sep 14, 2022 · 5 comments · Fixed by #243, #238, #253, #255 or #262
Closed
5 tasks

[Proposal] Minimal APIs ServiceBase Refactor #241

doddgu opened this issue Sep 14, 2022 · 5 comments · Fixed by #243, #238, #253, #255 or #262
Assignees
Labels
enhancement New feature or request
Milestone

Comments

@doddgu
Copy link
Contributor

doddgu commented Sep 14, 2022

假设标准服务写法

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

  1. TrimEnd:Service

DefaultTrimMethod:

  1. TrimStart:Get/Post/Create/Put/Update/Delete/Remove 等
  2. 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
  • 修改固定前缀
  • 自动注册方法
  • 个别破坏规则的需要单独指定
@doddgu
Copy link
Contributor Author

doddgu commented Sep 14, 2022

#238

@doddgu doddgu added the enhancement New feature or request label Sep 14, 2022
@zhenlei520
Copy link
Contributor

zhenlei520 commented Sep 14, 2022

此处为Route.Prefix = "api";

public class CatalogService: ServiceBase
{
    public CatalogService()
    {
        // 可有可无,设置后优先级将覆盖全局配置
        Route.Prefix = "api";
        Route.Version = "v2";
        Route.PluralizeServiceName = true;
    }
}

@zhenlei520
Copy link
Contributor

按照默认Route规则进行注册:api/v1/{controller}/{id?}/{method}

这里如果参数有id,默认注册为api/v1/{controller}/{id}api/v1/{controller}/{id?}

路由的规则多种多样,无法满足所有情况,如果需要的路由地址为api/v1/{controller}/{id?}/{method},则可以重写string GetMethodName(MethodInfo methodInfo)方法

@doddgu
Copy link
Contributor Author

doddgu commented Sep 15, 2022

已更新

@zhenlei520
Copy link
Contributor

In 0.6.0 this has been handled

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment