使用.Net6中的WebApplication打造最小API
.net6在preview4時(shí)給我們帶來(lái)了一個(gè)新的API:WebApplication,通過(guò)這個(gè)API我們可以打造更小的輕量級(jí)API服務(wù)。今天我們來(lái)嘗試一下如何使用WebApplication設(shè)計(jì)一個(gè)小型API服務(wù)系統(tǒng)。
環(huán)境準(zhǔn)備
- .NETSDK v6.0.0-preview.5.21355.2
- Visual Studio 2022 Preview
首先看看原始版本的WebApplication,官方已經(jīng)提供了樣例模板,打開(kāi)我們的vs2022,選擇新建項(xiàng)目選擇asp.net core empty,framework選擇.net6.0(preview)點(diǎn)擊創(chuàng)建,即可生成一個(gè)簡(jiǎn)單的最小代碼示例:

如果我們?cè)?csproj里在配置節(jié)PropertyGroup增加使用C#10新語(yǔ)法讓自動(dòng)進(jìn)行類型推斷來(lái)隱式的轉(zhuǎn)換成委托,則可以更加精簡(jiǎn):
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<LangVersion>preview</LangVersion>
</PropertyGroup>

當(dāng)然僅僅是這樣,是無(wú)法用于生產(chǎn)的,畢竟不可能所有的業(yè)務(wù)單元我們?nèi)M(jìn)這么一個(gè)小小的表達(dá)式里。不過(guò)借助WebApplication我們可以打造一個(gè)輕量級(jí)的系統(tǒng),可以滿足基本的依賴注入的小型服務(wù)。比如通過(guò)自定義特性類型,在啟動(dòng)階段告知系統(tǒng)為哪些服務(wù)注入哪些訪問(wèn)路徑,形成路由鍵和終結(jié)點(diǎn)。具體代碼如下:
首先我們創(chuàng)建一個(gè)簡(jiǎn)易的特性類,只包含httpmethod和path:
[AttributeUsage(AttributeTargets.Method)]
public class WebRouter : Attribute
{
public string path;
public HttpMethod httpMethod;
public WebRouter(string path)
{
this.path = path;
this.httpMethod = HttpMethod.Post;
}
public WebRouter(string path, HttpMethod httpMethod)
{
this.path = path;
this.httpMethod = httpMethod;
}
}
接著我們按照一般的分層設(shè)計(jì)一套DEMO應(yīng)用層/倉(cāng)儲(chǔ)服務(wù):
public interface IMyService
{
Task<MyOutput> Hello(MyInput input);
}
public interface IMyRepository
{
Task<bool> SaveData(MyOutput data);
}
public class MyService : IMyService
{
private readonly IMyRepository myRepository;
public MyService(IMyRepository myRepository)
{
this.myRepository = myRepository;
}
[WebRouter("/", HttpMethod.Post)]
public async Task<MyOutput> Hello(MyInput input)
{
var result = new MyOutput() { Words = $"hello {input.Name ?? "nobody"}" };
await myRepository.SaveData(result);
return await Task.FromResult(result);
}
}
public class MyRepository : IMyRepository
{
public async Task<bool> SaveData(MyOutput data)
{
Console.WriteLine($"保存成功:{data.Words}");
return await Task.FromResult(true);
}
}
最后我們需要將我們的服務(wù)接入到WebApplication的map里,怎么做呢?首先我們需要定義一套代理類型用來(lái)反射并獲取到具體的服務(wù)類型。這里為了簡(jiǎn)單的演示,我只設(shè)計(jì)包含一個(gè)入?yún)⒑蜎](méi)有入?yún)⒌那闆r下:
public abstract class DynamicPorxy
{
public abstract Delegate Instance { get; set; }
}
public class DynamicPorxyImpl<Tsvc, Timpl, Tinput, Toutput> : DynamicPorxy where Timpl : class where Tinput : class where Toutput : class
{
public override Delegate Instance { get; set; }
public DynamicPorxyImpl(MethodInfo method)
{
Instance = ([FromServices] IServiceProvider sp, Tinput input) => ExpressionTool.CreateMethodDelegate<Timpl, Tinput, Toutput>(method)(sp.GetService(typeof(Tsvc)) as Timpl, input);
}
}
public class DynamicPorxyImpl<Tsvc, Timpl, Toutput> : DynamicPorxy where Timpl : class where Toutput : class
{
public override Delegate Instance { get; set; }
public DynamicPorxyImpl(MethodInfo method)
{
Instance = ([FromServices] IServiceProvider sp) => ExpressionTool.CreateMethodDelegate<Timpl, Toutput>(method)(sp.GetService(typeof(Tsvc)) as Timpl);
}
}
接著我們創(chuàng)建一個(gè)代理工廠用于創(chuàng)建服務(wù)的方法委托并創(chuàng)建代理類型實(shí)例返回給調(diào)用端
public class DynamicPorxyFactory
{
public static IEnumerable<(WebRouter, DynamicPorxy)> RegisterDynamicPorxy()
{
foreach (var methodinfo in DependencyContext.Default.CompileLibraries.Where(x => !x.Serviceable && x.Type != "package")
.Select(x => AssemblyLoadContext.Default.LoadFromAssemblyName(new AssemblyName(x.Name)))
.SelectMany(x => x.GetTypes().Where(x => !x.IsInterface && x.GetInterfaces().Any()).SelectMany(x => x.GetMethods().Where(y => y.CustomAttributes.Any(z => z.AttributeType == typeof(WebRouter))))))
{
var webRouter = methodinfo.GetCustomAttributes(typeof(WebRouter), false).FirstOrDefault() as WebRouter;
DynamicPorxy dynamicPorxy;
if (methodinfo.GetParameters().Any())
dynamicPorxy = Activator.CreateInstance(typeof(DynamicPorxyImpl<,,,>).MakeGenericType(methodinfo.DeclaringType.GetInterfaces()[0], methodinfo.DeclaringType, methodinfo.GetParameters()[0].ParameterType , methodinfo.ReturnType), new object[] { methodinfo }) as DynamicPorxy;
else
dynamicPorxy = Activator.CreateInstance(typeof(DynamicPorxyImpl<,,>).MakeGenericType(methodinfo.DeclaringType.GetInterfaces()[0], methodinfo.DeclaringType, methodinfo.ReturnType), new object[] { methodinfo }) as DynamicPorxy;
yield return (webRouter, dynamicPorxy);
}
}
}
internal class ExpressionTool
{
internal static Func<TObj, Tin, Tout> CreateMethodDelegate<TObj, Tin, Tout>(MethodInfo method)
{
var mParameter = Expression.Parameter(typeof(TObj), "m");
var pParameter = Expression.Parameter(typeof(Tin), "p");
var mcExpression = Expression.Call(mParameter, method, Expression.Convert(pParameter, typeof(Tin)));
var reExpression = Expression.Convert(mcExpression, typeof(Tout));
return Expression.Lambda<Func<TObj, Tin, Tout>>(reExpression, mParameter, pParameter).Compile();
}
internal static Func<TObj, Tout> CreateMethodDelegate<TObj, Tout>(MethodInfo method)
{
var mParameter = Expression.Parameter(typeof(TObj), "m");
var mcExpression = Expression.Call(mParameter, method);
var reExpression = Expression.Convert(mcExpression, typeof(Tout));
return Expression.Lambda<Func<TObj, Tout>>(reExpression, mParameter).Compile();
}
}
最后我們創(chuàng)建WebApplication的擴(kuò)展方法來(lái)調(diào)用代理工廠以及注入IOC容器:
public static class WebApplicationBuilderExtension
{
static Func<string, Delegate, IEndpointConventionBuilder> GetWebApplicationMap(HttpMethod httpMethod, WebApplication webApplication) => (httpMethod) switch
{
(HttpMethod.Get) => webApplication.MapGet,
(HttpMethod.Post) => webApplication.MapPost,
(HttpMethod.Put) => webApplication.MapPut,
(HttpMethod.Delete) => webApplication.MapDelete,
_ => webApplication.MapGet
};
public static WebApplication RegisterDependencyAndMapDelegate(this WebApplicationBuilder webApplicationBuilder, Action<IServiceCollection> registerDependencyAction, Func<IEnumerable<(WebRouter webRouter, DynamicPorxy dynamicPorxy)>> mapProxyBuilder)
{
webApplicationBuilder.Host.ConfigureServices((ctx, services) =>
{
registerDependencyAction(services);
});
var webApplication = webApplicationBuilder.Build();
mapProxyBuilder().ToList().ForEach(item => GetWebApplicationMap(item.webRouter.httpMethod, webApplication)(item.webRouter.path, item.dynamicPorxy.Instance));
return webApplication;
}
}
當(dāng)然包括我們的自定義容器注入方法:
public class MyServiceDependency
{
public static void Register(IServiceCollection services)
{
services.AddScoped<IMyService, MyService>();
services.AddScoped<IMyRepository, MyRepository>();
}
}
最后改造我們的program.cs的代碼,通過(guò)擴(kuò)展來(lái)注入容器和代理委托并最終生成路由-終結(jié)點(diǎn):
await WebApplication.CreateBuilder().RegisterDependencyAndMapDelegate(MyServiceDependency.Register,DynamicPorxyFactory.RegisterDynamicPorxy).RunAsync("http://*:80");
這樣這套小型API系統(tǒng)就基本完成了,可以滿足日常的依賴注入和獨(dú)立的業(yè)務(wù)單元類型編寫(xiě),最后我們啟動(dòng)并調(diào)用一下,可以看到確實(shí)否符合我們的預(yù)期成功的調(diào)用到了應(yīng)用服務(wù)并且倉(cāng)儲(chǔ)也被正確的執(zhí)行了:

到此這篇關(guān)于使用.Net6中的WebApplication打造最小API的文章就介紹到這了。希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
在ASP.NET 2.0中操作數(shù)據(jù)之四十二:DataList和Repeater數(shù)據(jù)排序(一)
本文主要介紹利用ObjectDataSource的Selecting事件進(jìn)行DataList和Repeater數(shù)據(jù)排序的方法,DropDownList隱式的為我們將sort expression 和 direction保存在它的view state里,進(jìn)行分頁(yè)時(shí)從view state中取出條件進(jìn)行排序。2016-05-05
ASP.NET MVC的Localization本地化多語(yǔ)言支持
本文主要介紹在MVC中怎么使用ASP.NET中的資源文件做本地化的支持,希望對(duì)大家有所幫助。2016-04-04
在ASP.NET 2.0中操作數(shù)據(jù)之七十一:保護(hù)連接字符串及其它設(shè)置信息
默認(rèn)情況下,ASP.NET應(yīng)用程序數(shù)據(jù)庫(kù)連接字符串、用戶名和密碼等敏感信息都是保存在根目錄的web.config文件中,我們可以使用加密算法對(duì)其加密,從而保證這些敏感信息不被泄漏。2016-05-05
在ASP.NET 2.0中操作數(shù)據(jù)之二十六:排序自定義分頁(yè)數(shù)據(jù)
前面教程中我們實(shí)現(xiàn)了高效的自定義分頁(yè),但是只是一種固定排序方式。在本教程中,我們通過(guò)引入@sortExpression來(lái)擴(kuò)展存儲(chǔ)過(guò)程既實(shí)現(xiàn)自定義分頁(yè)又提供自定義排序的功能。2016-05-05
在ASP.NET中調(diào)用存儲(chǔ)過(guò)程方法新解
在ASP.NET中調(diào)用存儲(chǔ)過(guò)程方法新解...2006-10-10
NopCommerce架構(gòu)分析之(六)自定義RazorViewEngine和WebViewPage
本文對(duì)NopCommerce的后臺(tái)分離技術(shù)做簡(jiǎn)單的探討。NopCommerce通過(guò)自定義視圖引擎,重寫(xiě)了VirtualPathProviderViewEngine類的CreateView、CreatePartialView、FindView、FindPartialView方法,添加自定義的視圖搜索路徑來(lái)實(shí)現(xiàn)后臺(tái)分離。2016-04-04
解讀ASP.NET 5 & MVC6系列教程(5):Configuration配置信息管理
這篇文章主要介紹了ASP.NET 5中Configuration配置信息管理,需要的朋友可以參考下。2016-06-06
我今天開(kāi)始正式學(xué)習(xí).net遇到的問(wèn)題
我今天開(kāi)始正式學(xué)習(xí).net遇到的問(wèn)題...2006-10-10
解讀ASP.NET 5 & MVC6系列教程(17):MVC中的其他新特性
這篇文章主要介紹了ASP.NET 5中全局導(dǎo)入、獲取IP相關(guān)信息、文件上傳等其他新特性,需要的朋友可以參考下2016-06-06

