Biwen.QuickApi 項目介紹 [QuickApi("hello/world")] public class MyApi : BaseQuickApi<Req,Rsp>{} 提供一種簡單集成的Minimal Web Api交互模塊 遵循了 REPR 設計 (Request-Endpoint- ...
Biwen.QuickApi
項目介紹
[QuickApi("hello/world")]
public class MyApi : BaseQuickApi<Req,Rsp>{}
- 提供一種簡單集成的Minimal Web Api交互模塊 遵循了 REPR 設計 (Request-Endpoint-Response)
- 開箱即用的Api路由 和 許可權,Bind,validator體驗
- 該庫是NET WebApi/Minimal Api的補充,性能≈MinimalApi,遙遙領先於MVC和WebApi,但是提供了最簡單的的使用體驗
- write less, do more ; write anywhere, do anything
- 歡迎小伙伴們star&issue共同學習進步 (Biwen.QuickApi)[https://github.com/vipwan/Biwen.QuickApi]
使用方式
Step0 Nuget Install
dotnet add package Biwen.QuickApi
Step1 UseBiwenQuickApis
builder.Services.AddBiwenQuickApis(o =>
{
o.RoutePrefix = "quick";
//不需要駝峰模式設置為null
//o.JsonSerializerOptions.PropertyNamingPolicy = null;
});
//....
app.MapBiwenQuickApis();
Step2 Define Request and Response
public class HelloApiRequest : BaseRequest<HelloApiRequest>
{
public string? Name { get; set; }
/// <summary>
/// 別名綁定欄位
/// </summary>
[AliasAs("a")]
public string? Alias { get; set; }
public HelloApiRequest()
{
RuleFor(x => x.Name).NotNull().Length(5, 10);
}
}
/// <summary>
/// 模擬自定義綁定的Request
/// </summary>
public class CustomApiRequest : BaseRequest<CustomApiRequest>
{
public string? Name { get; set; }
public CustomApiRequest()
{
RuleFor(x => x.Name).NotNull().Length(5, 10);
}
}
/// <summary>
/// 自定義的綁定器
/// </summary>
public class CustomApiRequestBinder : IReqBinder<CustomApiRequest>
{
public async Task<CustomApiRequest> BindAsync(HttpContext context)
{
var request = new CustomApiRequest
{
Name = context.Request.Query["c"]
};
await Task.CompletedTask;
return request;
}
}
public class HelloApiResponse : BaseResponse
{
public string? Message { get; set; }
}
Step3 Define QuickApi
/// <summary>
/// get ~/admin/index
/// </summary>
[QuickApi("index", Group = "admin", Verbs = Verb.GET | Verb.POST, Policy = "admin")]
public class NeedAuthApi : BaseQuickApi
{
public override EmptyResponse Execute(EmptyRequest request)
{
return EmptyResponse.Instance;
}
}
/// <summary>
/// get ~/hello/world/{name}
/// </summary>
[QuickApi("world/{name}", Group = "hello", Verbs = Verb.GET | Verb.POST)]
public class HelloApi : BaseQuickApi<HelloApiRequest, HelloApiResponse>
{
private readonly HelloService _service;
private readonly IHttpContextAccessor _httpContextAccessor;
public Hello4Api(HelloService service,IHttpContextAccessor httpContextAccessor)
{
_service = service;
_httpContextAccessor = httpContextAccessor;
}
public override HelloApiResponse Execute(HelloApiRequest request)
{
var hello = _service.Hello($"hello world {_httpContextAccessor.HttpContext!.Request.Path} !");
return new HelloApiResponse
{
Message = hello
};
}
}
/// <summary>
/// get ~/custom?c=11112222
/// </summary>
[QuickApi("custom", Verbs = Verb.GET)]
public class CustomApi : BaseQuickApi<CustomApiRequest>
{
public CustomApi()
{
UseReqBinder<CustomApiRequestBinder>();
}
public override async Task<EmptyResponse> ExecuteAsync(CustomApiRequest request)
{
await Task.CompletedTask;
Console.WriteLine($"獲取自定義的 CustomApi:,從querystring:c綁定,{request.Name}");
return EmptyResponse.New;
}
/// <summary>
/// 提供minimal擴展
/// </summary>
/// <param name="builder"></param>
/// <returns></returns>
public override RouteHandlerBuilder HandlerBuilder(RouteHandlerBuilder builder)
{
//自定義描述
builder.WithOpenApi(operation => new(operation)
{
Summary = "This is a summary",
Description = "This is a description"
});
//自定義標簽
builder.WithTags("custom");
//自定義過濾器
builder.AddEndpointFilter(async (context, next) =>
{
Console.WriteLine("自定義過濾器!");
return await next(context);
});
//自定義Api版本
//預設為版本1.0,如果需要訪問其他版本,需要在querystring中添加?api-version=2.0 :)
builder.HasApiVersion(1.0).WithGroupName("1.0");
builder.HasApiVersion(2.0).WithGroupName("2.0");
return builder;
}
}
Step4 Enjoy !
//直接訪問
// GET ~/hello/world/biwen
// GET ~/hello/world/biwen?name=biwen
// POST ~/hello/world/biwen
// GET ~/custom?c=11112222
//你也可以把QuickApi當Service使用
app.MapGet("/fromapi", async (Biwen.QuickApi.DemoWeb.Apis.Hello4Api api) =>
{
//通過你的方式獲取請求對象
var req = new EmptyRequest();
//驗證請求對象
var result = req.RealValidator.Validate(req);
if (!result.IsValid)
{
return Results.BadRequest(result.ToDictionary());
}
//執行請求
var x = await api.ExecuteAsync(new EmptyRequest());
return Results.Ok(x);
});
Step5 OpenApi 以及Client代理
- 你可以全局配置版本號,以及自定義的OpenApi描述
- 你可以重寫QuickApi的HandlerBuilder方法,以便於你自定義的OpenApi描述
- 我們強烈建議您使用Refit風格直接擼介面,以便於您的客戶端和服務端保持一致的介面定義
- 因為遵循REPR風格,所以不推薦SwaggerUI或使用SwaggerStudio生成代理代碼,除非您的QuickApi定義的相當規範(如存在自定義綁定,別名綁定等)!
/// <summary>
/// refit client
/// </summary>
public interface IBusiness
{
[Refit.Get("/fromapi")]
public Task<TestRsp> TestPost();
}
//Refit
builder.Services.AddRefitClient<IBusiness>()
.ConfigureHttpClient(c => c.BaseAddress = new Uri("http://localhost:5101"));
var app = builder.Build();
app.MapGet("/from-quickapi", async (IBusiness bussiness) =>
{
var resp = await bussiness.TestPost();
return Results.Content(resp.Message);
});
Q&A
-
為什麼不支持多個參數的綁定?
-- 因為我認為這樣的Api設計是不合理的,我們遵循REPR設計理念,如果你需要多個參數,請使用複雜化的Request對象 -
QuickApi中如何拿到HttpContext對象?
-- 請在構造函數中註入IHttpContextAccessor獲取 -
是否支持Minimal的中間件和攔截器?
-- 支持的,本身QuickApi就是擴展了MinimalApi,底層也是Minimal的處理機制,所以請考慮全局的中間件和攔截器,以及重寫QuickApi的HandlerBuilder方法