ASP.NET Core依賴注入系列教程之服務(wù)的注冊(cè)與提供
前言
在采用了依賴注入的應(yīng)用中,我們總是直接利用DI容器直接獲取所需的服務(wù)實(shí)例,換句話說(shuō),DI容器起到了一個(gè)服務(wù)提供者的角色,它能夠根據(jù)我們提供的服務(wù)描述信息提供一個(gè)可用的服務(wù)對(duì)象。ASP.NET Core中的DI容器體現(xiàn)為一個(gè)實(shí)現(xiàn)了IServiceProvider接口的對(duì)象。
ServiceProvider與ServiceDescriptor
服務(wù)的注冊(cè)與提供
利用ServiceProvider來(lái)提供服務(wù)
提供一個(gè)服務(wù)實(shí)例的集合
獲取ServiceProvider自身對(duì)象
對(duì)泛型的支持
一、ServiceProvider與ServiceDescriptor
我一直覺(jué)得優(yōu)秀的設(shè)計(jì)首先應(yīng)該是簡(jiǎn)單的設(shè)計(jì),至少是看起來(lái)簡(jiǎn)單的設(shè)計(jì),這就是我們所謂的大道至簡(jiǎn)。作為一個(gè)服務(wù)的提供者,ASP.NET Core中的DI容器最終體現(xiàn)為一個(gè)IServiceProvider接口,我們將所有實(shí)現(xiàn)了該接口的類型及其實(shí)例統(tǒng)稱為ServiceProvider。如下面的代碼片段所示,該接口簡(jiǎn)單至極,它僅僅提供了唯一個(gè)GetService方法,該方法根據(jù)提供的服務(wù)類型為你提供對(duì)應(yīng)的服務(wù)實(shí)例。
public interface IServiceProvider { object GetService(Type serviceType); }
ASP.NET Core內(nèi)部真正使用的是一個(gè)實(shí)現(xiàn)了IServiceProvider接口的內(nèi)部類型(該類型的名稱為“ServiceProvider”),我們不能直接創(chuàng)建該對(duì)象,只能間接地通過(guò)調(diào)用IServiceCollection接口的擴(kuò)展方法BuildServiceProvider得到它。IServiceCollection接口定義在“Microsoft.Extensions.DependencyInjection”命名空間下,如果沒(méi)有特別說(shuō)明,本系列文章涉及到的與ASP.NET Core依賴注入相關(guān)的類型均采用此命名空間。 如下面的代碼片段所示,IServiceCollection接口實(shí)際上代表一個(gè)元素為ServiceDescriptor對(duì)象的集合,它直接繼承了另一個(gè)接口IList<ServiceDescriptor>,而ServiceCollection類實(shí)現(xiàn)了該接口。
public static class ServiceCollectionExtensions { public static IServiceProvider BuildServiceProvider(this IServiceCollection services); } public interface IServiceCollection : IList<ServiceDescriptor> {} Public class ServiceCollection: IServiceCollection { //省略成員 }
體現(xiàn)為DI容器的ServiceProvider之所以能夠根據(jù)我們給定的服務(wù)類型(一般是一個(gè)接口類型)提供一個(gè)能夠開(kāi)箱即用的服務(wù)實(shí)例,是因?yàn)槲覀冾A(yù)先注冊(cè)了相應(yīng)的服務(wù)描述信息,這些指導(dǎo)ServiceProvider正確實(shí)施服務(wù)提供操作的服務(wù)描述體現(xiàn)為如下一個(gè)ServiceDescriptor類型。
public class ServiceDescriptor { public ServiceDescriptor(Type serviceType, object instance); public ServiceDescriptor(Type serviceType, Func<IServiceProvider, object> factory, ServiceLifetime lifetime); public ServiceDescriptor(Type serviceType, Type implementationType, ServiceLifetime lifetime); public Type ServiceType { get; } public ServiceLifetime Lifetime { get; } public Type ImplementationType { get; } public object ImplementationInstance { get; } public Func<IServiceProvider, object> ImplementationFactory { get; } }
ServiceDescriptor的ServiceType屬性代表提供服務(wù)的生命類型,由于標(biāo)準(zhǔn)化的服務(wù)一般會(huì)定義成接口,所以在絕大部分情況下體現(xiàn)為一個(gè)接口類型。類型為ServiceLifetime的屬性Lifetime體現(xiàn)了ServiceProvider針對(duì)服務(wù)實(shí)例生命周期的控制方式。如下面的代碼片段所示,ServiceLifetime是一個(gè)美劇類型,定義其中的三個(gè)選項(xiàng)(Singleton、Scoped和Transient)體現(xiàn)三種對(duì)服務(wù)對(duì)象生命周期的控制形式,我們將在本節(jié)后續(xù)部分對(duì)此作專門(mén)的介紹。
public enum ServiceLifetime { Singleton, Scoped, Transient }
3-10對(duì)于ServiceDescriptor的其他三個(gè)屬性來(lái)說(shuō),它們實(shí)際上是輔助ServiceProvider完成具體的服務(wù)實(shí)例提供操。ImplementationType屬性代表被提供服務(wù)實(shí)例的真實(shí)類型,屬性ImplementationInstance則直接代表被提供的服務(wù)實(shí)例,ImplementationFactory則提供了一個(gè)創(chuàng)建服務(wù)實(shí)例的委托對(duì)象。ASP.NET Core與依賴注入相關(guān)的幾個(gè)核心類型具有如圖10所示的關(guān)系。
由于ASP.NET Core中的ServiceProvider是根據(jù)一個(gè)代表ServiceDescriptor集合的IServiceCollection對(duì)象創(chuàng)建的,當(dāng)我們調(diào)用其GetService方法的時(shí)候,它會(huì)根據(jù)我們提供的服務(wù)類型找到對(duì)應(yīng)的ServiceDecriptor對(duì)象。如果該ServiceDecriptor對(duì)象的ImplementationInstance屬性返回一個(gè)具體的對(duì)象,該對(duì)象將直接用作被提供的服務(wù)實(shí)例。如果ServiceDecriptor對(duì)象的ImplementationFactory返回一個(gè)具體的委托,該委托對(duì)象將直接用作創(chuàng)建服務(wù)實(shí)例的工廠。
如果這兩個(gè)屬性均為Null,ServiceProvider才會(huì)根據(jù)ImplementationType屬性返回的類型調(diào)用相應(yīng)的構(gòu)造函數(shù)創(chuàng)建被提供的服務(wù)實(shí)例。至于我們?cè)谏厦嬉还?jié)中提到的三種依賴注入方式,ServiceProvider僅僅支持構(gòu)造器注入,屬性注入和方法注入的支持并未提供。
二、服務(wù)的注冊(cè)與提供
ASP.NET Core針對(duì)依賴注入的編程主要體現(xiàn)在兩個(gè)方面:其一,創(chuàng)建一個(gè)ServiceCollection對(duì)象并將服務(wù)注冊(cè)信息以ServiceDescriptor對(duì)象的形式添加其中;其二,針對(duì)ServiceCollection對(duì)象創(chuàng)建對(duì)應(yīng)的ServiceProvider并利用它提供我們需要的服務(wù)實(shí)例。
在進(jìn)行服務(wù)注冊(cè)的時(shí)候,我們可以直接調(diào)用相應(yīng)的構(gòu)造函數(shù)創(chuàng)建ServiceDescriptor對(duì)象并將其添加到ServiceCollection對(duì)象之中。除此之外,IServiceCollection接口還具有如下三組擴(kuò)展方法將這兩個(gè)步驟合二為一。從下面給出的代碼片段我們不難看出這三組擴(kuò)展方法分別針對(duì)上面我們提及的三種針對(duì)服務(wù)實(shí)例的生命周期控制方式,泛型參數(shù)TService代表服務(wù)的聲明類型,即ServiceDescriptor的ServiceType屬性,至于ServiceDescriptor的其他屬性,則通過(guò)方法相應(yīng)的參數(shù)來(lái)提供。
public static class ServiceCollectionExtensions { public static IServiceCollection AddScoped<TService>(this IServiceCollection services) where TService: class; //其他AddScoped<TService>重載 public static IServiceCollection AddSingleton<TService>(this IServiceCollection services) where TService: class; //其他AddSingleton<TService>重載 public static IServiceCollection AddTransient<TService>(this IServiceCollection services) where TService: class; //其他AddTransient<TService>重載 }
對(duì)于用作DI容器的ServiceProvider對(duì)象來(lái)說(shuō),我們可以直接調(diào)用它的GetService方法根據(jù)指定的服務(wù)類型獲得想用的服務(wù)實(shí)例。除此之外,服務(wù)的提供還可以通過(guò)IServiceProvider接口相應(yīng)的擴(kuò)展方法來(lái)完成。如下面的代碼片段所示,擴(kuò)展方法GetService<T>以泛型參數(shù)的形式指定服務(wù)的聲明類型。至于另外兩個(gè)擴(kuò)展方法GetRequiredService和GetRequiredService<T>,如果ServiceProvider不能提供一個(gè)具體的服務(wù)實(shí)例,一個(gè)InvalidOperationException異常會(huì)被拋出來(lái)并提示相應(yīng)的服務(wù)注冊(cè)信息不足。
public static class ServiceProviderExtensions { public static T GetService<T>(this IServiceProvider provider); public static object GetRequiredService(this IServiceProvider provider, Type serviceType); public static T GetRequiredService<T>(this IServiceProvider provider); }
利用ServiceProvider來(lái)提供服務(wù)
接下來(lái)采用實(shí)例演示的方式來(lái)介紹如何利用ServiceCollection進(jìn)行服務(wù)注冊(cè),以及如何利用ServiceCollection創(chuàng)建對(duì)應(yīng)的ServiceProvider來(lái)提供我們需要的服務(wù)實(shí)例。我們創(chuàng)建一個(gè)ASP.NET Core控制臺(tái)程序,并在project.json中按照如下的方式添加針對(duì) “Microsoft.Extensions.DepedencyInjection”這個(gè)NuGet包的依賴。
{ "dependencies": { "Microsoft.Extensions.DependencyInjection": "1.0.0-rc1-final" }, ... }
我們接下來(lái)定義四個(gè)服務(wù)接口(IFoo、IBar、IBaz和IGux)以及分別實(shí)現(xiàn)它們的四個(gè)服務(wù)類(Foo、Bar、Baz和Gux)如下面的代碼片段所示,IGux具有三個(gè)只讀屬性(Foo、Bar和Baz)均為接口類型,并在構(gòu)造函數(shù)中進(jìn)行初始化。
public interface IFoo {} public interface IBar {} public interface IBaz {} public interface IGux { IFoo Foo { get; } IBar Bar { get; } IBaz Baz { get; } } public class Foo : IFoo {} public class Bar : IBar {} public class Baz : IBaz {} public class Gux : IGux { public IFoo Foo { get; private set; } public IBar Bar { get; private set; } public IBaz Baz { get; private set; } public Gux(IFoo foo, IBar bar, IBaz baz) { this.Foo = foo; this.Bar = bar; this.Baz = baz; } }
現(xiàn)在我們?cè)谧鳛槌绦蛉肟诘腗ain方法中創(chuàng)建了一個(gè)ServiceCollection對(duì)象,并采用不同的方式完成了針對(duì)四個(gè)服務(wù)接口的注冊(cè)。具體來(lái)說(shuō),對(duì)于正對(duì)服務(wù)接口IFoo和IGux的ServiceDescriptor來(lái)說(shuō),我們指定了代表服務(wù)真實(shí)類型的ImplementationType屬性,而對(duì)于針對(duì)服務(wù)接口IBar和IBaz的ServiceDescriptor來(lái)說(shuō),我們初始化的則是分別代表服務(wù)實(shí)例和服務(wù)工廠的ImplementationInstance個(gè)ImplementationFactory屬性。由于我們調(diào)用的是AddSingleton方法,所以四個(gè)ServiceDescriptor的Lifetime屬性均為Singleton。
class Program { static void Main(string[] args) { IServiceCollection services = new ServiceCollection() .AddSingleton<IFoo, Foo>() .AddSingleton<IBar>(new Bar()) .AddSingleton<IBaz>(_ => new Baz()) .AddSingleton<IGux, Gux>(); IServiceProvider serviceProvider = services.BuildServiceProvider(); Console.WriteLine("serviceProvider.GetService<IFoo>(): {0}",serviceProvider.GetService<IFoo>()); Console.WriteLine("serviceProvider.GetService<IBar>(): {0}", serviceProvider.GetService<IBar>()); Console.WriteLine("serviceProvider.GetService<IBaz>(): {0}", serviceProvider.GetService<IBaz>()); Console.WriteLine("serviceProvider.GetService<IGux>(): {0}", serviceProvider.GetService<IGux>()); } }
接下來(lái)我們調(diào)用ServiceCollection對(duì)象的擴(kuò)展方法BuildServiceProvider得到對(duì)應(yīng)的ServiceProvider對(duì)象,然后調(diào)用其擴(kuò)展方法GetService<T>分別獲得針對(duì)四個(gè)接口的服務(wù)實(shí)例對(duì)象并將類型名稱其輸出到控制臺(tái)上。運(yùn)行該程序之后,我們會(huì)在控制臺(tái)上得到如下的輸出結(jié)果,由此印證ServiceProvider為我們提供了我們期望的服務(wù)實(shí)例。
serviceProvider.GetService<IFoo>(): Foo serviceProvider.GetService<IBar>(): Bar serviceProvider.GetService<IBaz>(): Baz serviceProvider.GetService<IGux>(): Gux
提供一個(gè)服務(wù)實(shí)例的集合
如果我們?cè)谡{(diào)用GetService方法的時(shí)候?qū)⒎?wù)類型指定為IEnumerable<T>,那么返回的結(jié)果將會(huì)是一個(gè)集合對(duì)象。除此之外, 我們可以直接調(diào)用IServiceProvider如下兩個(gè)擴(kuò)展方法GetServeces達(dá)到相同的目的。在這種情況下,ServiceProvider將會(huì)利用所有與指定服務(wù)類型相匹配的ServiceDescriptor來(lái)提供具體的服務(wù)實(shí)例,這些均會(huì)作為返回的集合對(duì)象的元素。如果所有的ServiceDescriptor均與指定的服務(wù)類型不匹配,那么最終返回的是一個(gè)空的集合對(duì)象。
public static class ServiceProviderExtensions { public static IEnumerable<T> GetServices<T>(this IServiceProvider provider); public static IEnumerable<object> GetServices(this IServiceProvider provider, Type serviceType); }
值得一提的是,如果ServiceProvider所在的ServiceCollection包含多個(gè)具有相同服務(wù)類型(對(duì)應(yīng)ServiceType屬性)的ServiceDescriptor,當(dāng)我們調(diào)用GetService方法獲取單個(gè)服務(wù)實(shí)例的時(shí)候,只有最后一個(gè)ServiceDescriptor才是有效的,至于其他的ServiceDescriptor,它們只有在獲取服務(wù)集合的場(chǎng)景下才有意義。
我們通過(guò)一個(gè)簡(jiǎn)單的實(shí)例來(lái)演示如何利用ServiceProvider得到一個(gè)包含多個(gè)服務(wù)實(shí)例的集合。我們?cè)谝粋€(gè)控制臺(tái)應(yīng)用中定義了如下一個(gè)服務(wù)接口IFoobar,兩個(gè)服務(wù)類型Foo和Bar均實(shí)現(xiàn)了這個(gè)接口。在作為程序入口的Main方法中,我們將針針對(duì)服務(wù)類型Foo和Bar的兩個(gè)ServiceDescriptor添加到創(chuàng)建的ServiceCollection對(duì)象中,這兩個(gè)ServiceDescriptor對(duì)象的ServiceType屬性均為IFoobar。
class Program { static void Main(string[] args) { IServiceCollection serviceCollection = new ServiceCollection() .AddSingleton<IFoobar, Foo>() .AddSingleton<IFoobar, Bar>(); IServiceProvider serviceProvider = serviceCollection.BuildServiceProvider(); Console.WriteLine("serviceProvider.GetService<IFoobar>(): {0}", serviceProvider.GetService<IFoobar>()); IEnumerable<IFoobar> services = serviceProvider.GetServices<IFoobar>(); int index = 1; Console.WriteLine("serviceProvider.GetServices<IFoobar>():"); foreach (IFoobar foobar in services) { Console.WriteLine("{0}: {1}", index++, foobar); } } } public interface IFoobar {} public class Foo : IFoobar {} public class Bar : IFoobar {}
在調(diào)用ServiceCollection對(duì)象的擴(kuò)展方法BuildServiceProvider得到對(duì)應(yīng)的ServiceProvider對(duì)象之后,我們先調(diào)用其GetService<T>方法以確定針對(duì)服務(wù)接口IFoobar得到的服務(wù)實(shí)例的真實(shí)類型就是是Foo還是Bar。接下來(lái)我們調(diào)用ServiceProvider的擴(kuò)展方法GetServices<T>獲取一組針對(duì)服務(wù)接口IFoobar的服務(wù)實(shí)例并將它們的真是類型打印在控制臺(tái)上。該程序運(yùn)行后將會(huì)在控制臺(tái)上生成如下的輸出結(jié)果。
serviceProvider.GetService<IFoobar>(): Bar serviceProvider.GetServices<IFoobar>(): 1: Foo 2: Bar
獲取ServiceProvider自身對(duì)象
對(duì)于ServiceProvider的服務(wù)提供機(jī)制來(lái)說(shuō),還有一個(gè)小小的細(xì)節(jié)值得我們關(guān)注,那就是當(dāng)我們調(diào)用GetService或者GetRequiredService方法的時(shí)候若將服務(wù)類型設(shè)定為IServiceProvider,那么得到的對(duì)象實(shí)際上就是ServiceProvider自身這個(gè)對(duì)象。與之同理,調(diào)用GetServices方法將會(huì)返回一個(gè)包含自身的集合。如下所示的代碼片段體現(xiàn)了ServiceProvider的這個(gè)特性。
class Program { static void Main(string[] args) { IServiceProvider serviceProvider = new ServiceCollection().BuildServiceProvider(); Debug.Assert(object.ReferenceEquals(serviceProvider, serviceProvider.GetService<IServiceProvider>())); Debug.Assert(object.ReferenceEquals(serviceProvider, serviceProvider.GetServices<IServiceProvider>().Single())); } }
對(duì)泛型的支持
ServiceProvider提供的服務(wù)實(shí)例不僅限于普通的類型,它對(duì)泛型服務(wù)類型同樣支持。在針對(duì)泛型服務(wù)進(jìn)行注冊(cè)的時(shí)候,我們可以將服務(wù)類型設(shè)定為攜帶具體泛型參數(shù)的“關(guān)閉泛型類型”(比如IFoobar<IFoo,IBar>),除此之外服務(wù)類型也可以是包含具體泛型參數(shù)的“開(kāi)放泛型類型”(比如IFoo<,>)。前者實(shí)際上還是將其視為非泛型服務(wù)來(lái)對(duì)待,后者才真正體現(xiàn)了“泛型”的本質(zhì)。
比如我們注冊(cè)了某個(gè)泛型服務(wù)接口IFoobar<,>與它的實(shí)現(xiàn)類Foobar<,>之間的映射關(guān)系,當(dāng)我們指定一個(gè)攜帶具體泛型參數(shù)的服務(wù)接口類型IFoobar<IFoo,IBar>并調(diào)用ServiceProvider的GetService方法獲取對(duì)應(yīng)的服務(wù)實(shí)例時(shí),ServiceProvider會(huì)針對(duì)指定的泛型參數(shù)類型(IFoo和IBar)來(lái)解析與之匹配的實(shí)現(xiàn)類型(可能是Foo和Baz)并得到最終的實(shí)現(xiàn)類型(Foobar<Foo,Baz>)。
我們同樣利用一個(gè)簡(jiǎn)單的控制臺(tái)應(yīng)用來(lái)演示基于泛型的服務(wù)注冊(cè)與提供方式。如下面的代碼片段所示,我們定義了三個(gè)服務(wù)接口(IFoo、IBar和IFoobar<T1,T2>)和實(shí)現(xiàn)它們的三個(gè)服務(wù)類(Foo、Bar個(gè)Foobar<T1,T2>),泛型接口具有兩個(gè)泛型參數(shù)類型的屬性(Foo和Bar),它們?cè)趯?shí)現(xiàn)類中以構(gòu)造器注入的方式被初始化。
class Program { static void Main(string[] args) { IServiceProvider serviceProvider = new ServiceCollection() .AddTransient<IFoo, Foo>() .AddTransient<IBar, Bar>() .AddTransient(typeof(IFoobar<,>), typeof(Foobar<,>)) .BuildServiceProvider(); Console.WriteLine("serviceProvider.GetService<IFoobar<IFoo, IBar>>().Foo: {0}", serviceProvider.GetService<IFoobar<IFoo, IBar>>().Foo); 12: Console.WriteLine("serviceProvider.GetService<IFoobar<IFoo, IBar>>().Bar: {0}", serviceProvider.GetService<IFoobar<IFoo, IBar>>().Bar); } } public interface IFoobar<T1, T2> { T1 Foo { get; } T2 Bar { get; } } public interface IFoo {} public interface IBar {} public class Foobar<T1, T2> : IFoobar<T1, T2> { public T1 Foo { get; private set; } public T2 Bar { get; private set; } public Foobar(T1 foo, T2 bar) { this.Foo = foo; this.Bar = bar; } } public class Foo : IFoo {} public class Bar : IBar {}
在作為入口程序的Main方法中,我們創(chuàng)建了一個(gè)ServiceCollection對(duì)象并采用Transient模式注冊(cè)了上述三個(gè)服務(wù)接口與對(duì)應(yīng)實(shí)現(xiàn)類型之間的映射關(guān)系,對(duì)于泛型服務(wù)IFoobar<T1,T2>/Foobar<T1,T2>來(lái)說(shuō),我們指定的是不攜帶具體泛型參數(shù)的開(kāi)放泛型類型IFoobar<,>/Foobar<,>。利用此ServiceCollection創(chuàng)建出對(duì)應(yīng)的ServiceProvider之后,我們調(diào)用后者的GetService方法并指定IFoobar<IFoo,IBar>為服務(wù)類型。得到的服務(wù)對(duì)象將會(huì)是一個(gè)Foobar<Foo,Bar>對(duì)象,我們將它的Foo和Bar屬性類型輸出于控制臺(tái)上作為驗(yàn)證。該程序執(zhí)行之后將會(huì)在控制臺(tái)上產(chǎn)生下所示的輸出結(jié)果。
serviceProvider.GetService<IFoobar<IFoo, IBar>>().Foo: Foo serviceProvider.GetService<IFoobar<IFoo, IBar>>().Bar: Bar
總結(jié)
以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,如果有疑問(wèn)大家可以留言交流,謝謝大家對(duì)腳本之家的支持。
相關(guān)文章
.NET內(nèi)存泄漏分析Windbg項(xiàng)目實(shí)例
這篇文章介紹了.NET內(nèi)存泄漏分析Windbg項(xiàng)目實(shí)例,文中通過(guò)示例代碼介紹的非常詳細(xì)。對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-12-12ASP.NET網(wǎng)站使用Kindeditor富文本編輯器配置步驟
首先下載編輯器然后部署編輯器最后在網(wǎng)頁(yè)中加入(ValidateRequest="false")引入腳本文件,具體配置步驟如下,有需求的朋友可以了解下哈2013-06-06在ASP.Net中實(shí)現(xiàn)RSA加密的方法
這篇文章介紹了在ASP.Net中實(shí)現(xiàn)RSA加密的方法,有需要的朋友可以參考一下2013-11-11Asp.net動(dòng)態(tài)生成html頁(yè)面的方法分享
這篇文章介紹了Asp.net動(dòng)態(tài)生成html頁(yè)面的方法,有需要的朋友可以參考一下2013-10-10gridview+objectdatasource+aspnetpager整合實(shí)例
gridview+objectdatasource+aspnetpager整合實(shí)例,需要的朋友可以參考一下2013-04-04asp.net 2.0 中的URL重寫(xiě)以及urlMappings問(wèn)題
asp.net 2.0 中的URL重寫(xiě)以及urlMappings問(wèn)題...2007-04-04詳解.NET?Core如何構(gòu)建一個(gè)彈性的HTTP請(qǐng)求機(jī)制
在分布式系統(tǒng)中,服務(wù)間的依賴關(guān)系復(fù)雜,任何一個(gè)服務(wù)的故障都可能導(dǎo)致整個(gè)系統(tǒng)的不可用,這時(shí)彈性?HTTP?請(qǐng)求機(jī)制就可以幫助我們,下面我們就來(lái)看看.NET?Core如何構(gòu)建一個(gè)彈性的HTTP請(qǐng)求機(jī)制吧2025-01-01理解ASP.NET Core 中間件(Middleware)
這篇文章主要介紹了ASP.NET Core 中間件(Middleware),中間件是一種裝配到應(yīng)用管道以處理請(qǐng)求和響應(yīng)的軟件。文中講解相關(guān)知識(shí)非常詳細(xì),感興趣的朋友可以一起來(lái)看一看2021-09-09