我在写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); }
总结:通过这种方式,代码结构已经清晰多了。若是有议,可以在评论区联系我。
发表评论