我在寫MinimalAPI的時候,發現不能最清晰的看到每個API,原因就是:WebAPI中不斷增長邏輯處理過程
於是我在想如何簡化API至一行,在一點一點想辦法中,發現了簡化DotNET Minimal API的方式。特此記錄下來這個思路給需要幫助的人。我的靈感來源於 C# 11 功能 - 介面中的靜態虛擬成員,透過靜態虛擬成員清晰整個API。
這是我思路的最終結果:在 Program.cs 中我們能透過一行程式碼,清晰的看到程式碼情況。
而無需指定平常不是很關心的處理過程和請求方式。
app.MapGroup("Connect", o =>
{
o.MapMethods<Authorize>("Authorize");
o.MapMethods<Authorize.Callback>("Authorize/Callback");
o.MapMethods<Token>("Token");
o.MapMethods<UserInfo>("UserInfo").RequireAuthorization(new AuthorizeAttribute()
{
AuthenticationSchemes = OpenIddictValidationAspNetCoreDefaults.AuthenticationScheme
});
o.MapMethods<Endsession>("Endsession");
});
app.MapGroup("Account", o =>
{
o.MapMethods<Login>("Login");
o.MapMethods<Externallogin>("Externallogin");
o.MapMethods<Externallogin.Callback>("Externallogin/Callback");
o.MapMethods<ConfirmationLogin>("ConfirmationLogin");
o.MapMethods<ForgotPassword>("ForgotPassword");
o.MapMethods<ResetPassword>("ResetPassword");
});
我們只需要簡單的三步就可以做到這個事情,並且可以不用反射和其他的複雜過程。
第一步,我們需要建立(附帶靜態抽象函式的介面)IEndpointBase。
public interface IEndpointBase
{
public static abstract IEnumerable<string> HTTPMethods();
public static abstract Delegate Handler();
}
第二步,需要實現IEndpointBase。
public class Login : IEndpointBase
{
public record AccountLoginRequest
{
[JsonPropertyName("u"), Required]
public string UserName { get; set; } = default!;
[JsonPropertyName("p"), Required]
public string Password { get; set; } = default!;
[JsonPropertyName("r"), Required]
public string ReturnUrl { get; set; } = default!;
[FromQuery]
public bool UseCookies { get; set; }
}
public static Delegate Handler()
{
var callback = async ([FromBody] AccountLoginRequest request, [FromServices] SignInManager signInManager) =>
{
// Todo: returnUrl validate is success
var result = await signInManager.PasswordSignInAsync(request.UserName, request.Password, request.UseCookies, lockoutOnFailure: true);
return Results.Text(result.ToString());
};
return callback;
}
public static IEnumerable<string> HTTPMethods() => [HttpMethods.Post];
}
第三步:處理靜態IEndpointBase,此時我們已經完成了這件事情。
public static RouteHandlerBuilder MapMethods<T>(this IEndpointRouteBuilder app, [StringSyntax("Route")] string pattern) where T : IEndpointBase
{
return app.MapMethods(pattern, T.HTTPMethods(), T.Handler());
}
單純的使用擴充套件好的 MapMethods 已經足夠清晰了。
但如果有對API進行分組的要求,使用原生的還是不會清晰,原因是:
-
要對 MapGroup 的值賦予變數名,比如說 var accountGroup = app.MapGroup("Account"),每個組都要想個名字。accountGroup
-
不能清楚自己的邊界,比如說 寫程式碼時,有可能出現插隊的情況,本來 accountGroup下面都是屬於它的端點,結果不小心插進來一個別的。
於是簡單的對MapGroup進行了擴充套件,最終結果在本文最上面。
public static void MapGroup(this IEndpointRouteBuilder endpoints, [StringSyntax("Route")] string prefix, Action<IEndpointRouteBuilder> action)
{
var group = endpoints.MapGroup(prefix);
action(group);
}
總結:透過這種方式,程式碼結構已經清晰多了。若是有議,可以在評論區聯絡我。