《asp.netcore6框架揭秘》实例演示[27]:asp.netcore6minimalapi的模拟实现

dotNET跨平台 dotNET跨平台     2022-12-02     187

关键词:

Minimal API仅仅是在基于IHost/IHostBuilder的服务承载系统上作了小小的封装而已,它利用WebApplication和WebApplicationBuilder这两个类型提供了更加简洁的API,同时提供了与现有API的兼容。[本文节选《ASP.NET Core 6框架揭秘》第17章]

一、基础模型
二、WebApplication
三、WebApplication的构建
     1. BootstrapHostBuilder
     2. ConfigureHostBuilder
     3. ConfigureWebHostBuilder
     4. WebApplicationBuilder
四、 工厂方法

一、基础模型

对于由WebApplication和WebApplicationBuilder构建的承载模型,我们没有必要了解其实现的每一个细节,知道其大致的设计和实现原理就可以了,所以本节会采用最简洁的代码模拟这两个类型的实现。如图1所示,代表承载应用的WebApplication对象是对一个IHost对象的封装,而且该类型自身也实现了IHost接口, WebApplication对象其实还是作为一个IHost对象被启动的。作为构建这的WebApplicationBuilder则是对一个IHostBuilder对象的封装,它对WebApplication对象的构建体现在利用封装的IHostBuilder对象构建一个对应的IHost对象,最终利用后者将WebApplication对象创建出来。

图17-8 完整的请求处理管道

二、WebApplication

WebApplication类型不仅仅实现了IHost接口,还同时实现IApplicationBuilder接口,所以中间件可以直接注册到这个对象上的。该类型还实现了IEndpointRouteBuilder接口,所以我们还能利用它进行路由注册,我们在20章才会涉及到路由,所以我们现在先忽略针对该接口的实现。下面的代码模拟WebApplication类型的实现。如代码片段所示,WebApplication的构造函数定义了一个IHost类型的参数,它利用这个对象完成了对IHost接口所有成员的实现,针对IApplicationBuilder接口成员的实现则利用创建的ApplicationBuilder对象来完成。WebApplication还提供了一个BuildRequestDelegate方法利用这个ApplicationBuilder对象完成了对中间件管道的构建。

public class WebApplication : IApplicationBuilder, IHost

    private readonly IHost _host;
    private readonly ApplicationBuilder _app;

    public WebApplication(IHost host)
    
        _host = host;
        _app = new ApplicationBuilder(host.Services);
    

    IServiceProvider IHost.Services 
        => _host.Services;
    Task IHost.StartAsync(CancellationToken cancellationToken) 
        => _host.StartAsync(cancellationToken);
    Task IHost.StopAsync(CancellationToken cancellationToken) 
        => _host.StopAsync(cancellationToken);

    IServiceProvider IApplicationBuilder.ApplicationServices 
     
        get => _app.ApplicationServices; 
        set => _app.ApplicationServices = value; 
    
    IFeatureCollection IApplicationBuilder.ServerFeatures 
        => _app.ServerFeatures;
    IDictionary<string, object?> IApplicationBuilder.Properties 
        => _app.Properties;
    RequestDelegate IApplicationBuilder.Build() 
        => _app.Build();
    IApplicationBuilder IApplicationBuilder.New() 
        => _app.New();
    IApplicationBuilder IApplicationBuilder
        .Use(Func<RequestDelegate, RequestDelegate> middleware) 
        => _app.Use(middleware);

    void IDisposable.Dispose() 
        => _host.Dispose();
    public IServiceProvider Services 
        => _host.Services;
    internal RequestDelegate BuildRequestDelegate() 
        => _app.Build();
    ...

WebApplication额外定义了如下的RunAsync和Run方法,它们分别以异步和同步方式启动承载的应用。调用这两个方法的时候可以指定监听地址,指定的地址被添加到IServerAddressesFeature特性中,而服务器正式利用这个特性来提供监听地址的。

public class WebApplication : IApplicationBuilder, IHost

    private readonly IHost _host;

    public ICollection<string> Urls 
        => _host.Services.GetRequiredService<IServer>()
        .Features.Get<IServerAddressesFeature>()?.Addresses 
        ?? throw new InvalidOperationException("IServerAddressesFeature is not found.");

    public Task RunAsync(string? url = null)
    
        Listen(url);
        return HostingAbstractionsHostExtensions.RunAsync(this);
    

    public void Run(string? url = null)
    
        Listen(url);
        HostingAbstractionsHostExtensions.Run(this);
    

    private void Listen(string? url)
    
        if (url is not null)
        
            var addresses = _host.Services
                .GetRequiredService<IServer>().Features
                .Get<IServerAddressesFeature>()?.Addresses 
                ?? throw new InvalidOperationException("IServerAddressesFeature is not found.");
            addresses.Clear();
            addresses.Add(url);
        
    
    ...

三、WebApplication的构建

要创建一个WebApplication对象,只需要提供一个对应的IHost对象即可。IHost对象是通过IHostBuilder对象构建的,所以WebApplicationBuilder需要一个IHostBuilder对象,具体来说是一个HostBuilder对象。我们针对WebApplicationBuilder对象所作的一切设置最终都需要转移到这个HostBuilder对象上才能生效。为了提供更加简洁的API,WebApplicationBuilder类型提供了一系列的属性。比如它利用Serrvices属性提供了可以直接进行服务注册的IServiceCollection集合,利用Environment属性提供了表示当前承载环境的IWebHostEnvironment对象,利用Configuration属性提供的ConfigurationManager对象不仅可以作为IConfigurationBuilder对象帮助我们完成对配置系统的一切设置,它自身也可以作为IConfiguration对象为我们提供配置。

WebApplicationBuilder还定义了Host和WebHost属性,对应类型为ConfigureHostBuilder和ConfigureWebHostBuilder,它们分别实现了IHostBuilder和IWebHostBuilder接口,其目的是为了复用IHostBuilder和IWebHostBuilder接口承载的API(主要是扩展方法)。为了会尽可能使用现有方法对IHostBuilder对象进行初始化设置,它还使用了一个实现了IHostBuilder接口的BootstrapHostBuilder类型。有这些对象组成了WebApplicationBuilder针对HostBuilder的构建模型。如图2所示,WebApplicationBuilder的所有工作都是为了构建它封装的HostBuilder对象。

当WebApplicationBuilder初始化的时候,它除了会创建这个HostBuilder对象,还会创建存储服务注册的IServiceCollection对象,以及用来对配置进行设置的ConfigurationManager对象。接下来它会创建一个BootstrapHostBuilder对象,并将它参数调用相应的方法(比如ConfigureWebHostDefaults方法)将初始化设置收集起来,收集的服务注册和针对配置系统的设置分别转移到创建的IServiceCollection和ConfigurationManager对象中,其他设置直接应用到封装的HostBuilder对象上。

图2 HostBuilder构建模型

WebApplicationBuilder在此之后会创建出代表承载环境的IWebHostEnvironment对象,并对Environment属性进行初始化。在得到表示承载上下文的WebHostBuilderContext对象之后,上述的ConfigureHostBuilder和ConfigureWebHostBuilder对象被创建出来,并赋值给Host和WebHost属性。与BootstrapHostBuilder作用类似,我们利用这两个对象所作的设置最终都会转移到上述的三个对象中。当WebApplicationBuilder进行WebApplication对象构建的时候,IServiceCollection对象存储的服务注册和ConfigurationManager对象承载配置最终转移到HostBuilder对象上。此时再利用后者构建出对应的IHost对象,代表承载应用的WebApplication对象最终由该对象构建出来。

1. BootstrapHostBuilder

如下所示的是我们模拟的BootstrapHostBuilder类型的定义。正如上面所说,这个它的作用是收集初始化IHostBuilder对象提供的设置并将它们分别应用到指定的IServiceCollection、ConfigurationManager和IHostBuilder对象上。这一使命体现在BootstrapHostBuilder的Apply方法上,该方法还通过一个输出参数返回创建的HostBuilderContext上下文。

public class BootstrapHostBuilder : IHostBuilder

    private readonly List<Action<IConfigurationBuilder>> _configureHostConfigurations = new();
    private readonly List<Action<HostBuilderContext, IConfigurationBuilder>> _configureAppConfigurations = new();
    private readonly List<Action<HostBuilderContext, IServiceCollection>> _configureServices = new();
    private readonly List<Action<IHostBuilder>> _others = new();

    public IDictionary<object, object> Properties  get;  
        = new Dictionary<object, object>();
    public IHost Build() 
        => throw new NotImplementedException();
    public IHostBuilder ConfigureHostConfiguration(
        Action<IConfigurationBuilder> configureDelegate)
    
        _configureHostConfigurations.Add(configureDelegate);
        return this;
    
    public IHostBuilder ConfigureAppConfiguration(
        Action<HostBuilderContext, IConfigurationBuilder> configureDelegate)
    
        _configureAppConfigurations.Add(configureDelegate);
        return this;
    
    public IHostBuilder ConfigureServices(
        Action<HostBuilderContext, IServiceCollection> configureDelegate)
    
        _configureServices.Add(configureDelegate);
        return this;
    
    public IHostBuilder UseServiceProviderFactory<TContainerBuilder>(
        IServiceProviderFactory<TContainerBuilder> factory)
    
        _others.Add(builder => builder.UseServiceProviderFactory(factory));
        return this;
    
    public IHostBuilder UseServiceProviderFactory<TContainerBuilder>(
        Func<HostBuilderContext, IServiceProviderFactory<TContainerBuilder>> factory)
    
        _others.Add(builder => builder.UseServiceProviderFactory(factory));
        return this;
    
    public IHostBuilder ConfigureContainer<TContainerBuilder>(
        Action<HostBuilderContext, TContainerBuilder> configureDelegate)
    
        _others.Add(builder => builder.ConfigureContainer(configureDelegate));
        return this;
    

    internal void Apply(
        IHostBuilder hostBuilder, 
        ConfigurationManager configuration,
        IServiceCollection services, 
        out HostBuilderContext builderContext)
    
        // 初始化针对宿主的配置
        var hostConfiguration = new ConfigurationManager();
        _configureHostConfigurations.ForEach( 
            it => it(hostConfiguration));

        // 创建承载环境
        var environment = new HostingEnvironment()
        
            ApplicationName = hostConfiguration[HostDefaults.ApplicationKey],
            EnvironmentName = hostConfiguration[HostDefaults.EnvironmentKey] ?? Environments.Production,
            ContentRootPath = HostingPathResolver.ResolvePath(hostConfiguration[HostDefaults.ContentRootKey])
        ;
        environment.ContentRootFileProvider 
            = new PhysicalFileProvider(environment.ContentRootPath);

        // 创建HostBuilderContext上下文
        var hostContext = new HostBuilderContext(Properties)
        
            Configuration = hostConfiguration,
            HostingEnvironment = environment,
        ;

        // 将针对宿主的配置添加到ConfigurationManager中
        configuration.AddConfiguration(hostConfiguration, true);

        // 初始化针对应用的配置
        _configureAppConfigurations.ForEach( 
            it => it(hostContext, configuration));

        // 收集服务注册
        _configureServices.ForEach( 
            it => it(hostContext, services));

        // 将针对依赖注入容器的设置应用到指定的IHostBuilder对象上
        _others.ForEach(it => it(hostBuilder));

        // 将自定义属性转移到指定的IHostBuilder对象上
        foreach (var kv in Properties)
        
            hostBuilder.Properties[kv.Key] = kv.Value;
        

        builderContext = hostContext;
    

除了Build方法,IHostBuilder接口中定义的所有方法的参数都是委托,所以实现的这些方法将提供的委托收集起来。在Apply方法中,我们通过执行这些委托对象,将初始化设置应用到指定的IServiceCollection、ConfigurationManager和IHostBuilder对象上,并根据初始化宿主配置构建出代表承载环境的HostingEnvironment对象。该方法最后根据承载环境结合配置将HostBuilderContext上下文创建出来,并以输出参数的形式返回。

internal static class HostingPathResolver

    public static string ResolvePath(string? contentRootPath) 
        => ResolvePath(contentRootPath, .BaseDirectory);
    public static string ResolvePath(string? contentRootPath, string basePath) 
        => string.IsNullOrEmpty(contentRootPath)
        ? Path.GetFullPath(basePath): Path.IsPathRooted(contentRootPath)? Path.GetFullPath(contentRootPath)
        : Path.GetFullPath(Path.Combine(Path.GetFullPath(basePath), contentRootPath));

2. ConfigureHostBuilder

ConfigureHostBuilder是在应用了BootstrapHostBuilder收集的初始化设置之后创建的,在创建该对象时提供了HostBuilderContext上下文, ConfigurationManager和IServiceCollection对象。提供的服务注册直接添加到IServiceCollection对象中,针对配置的设置已经应用到ConfigurationManager对象,直接针对IHostBuilder对象的设置则利用_configureActions字段暂存起来。

public class ConfigureHostBuilder : IHostBuilder

    private readonly ConfigurationManager _configuration;
    private readonly IServiceCollection _services;
    private readonly HostBuilderContext _context;
    private readonly List<Action<IHostBuilder>> _configureActions = new();

    internal ConfigureHostBuilder(
        HostBuilderContext context, 
        ConfigurationManager configuration, 
        IServiceCollection services)
    
        _configuration = configuration;
        _services = services;
        _context = context;
    

    public IDictionary<object, object> Properties 
        => _context.Properties;
    public IHost Build() 
        => throw new NotImplementedException();
    public IHostBuilder ConfigureAppConfiguration(
        Action<HostBuilderContext, IConfigurationBuilder> configureDelegate)
        => Configure(() => configureDelegate(_context, _configuration));

    public IHostBuilder ConfigureHostConfiguration(
        Action<IConfigurationBuilder> configureDelegate)
    
        var applicationName = _configuration[HostDefaults.ApplicationKey];
        var contentRoot = _context.HostingEnvironment.ContentRootPath;
        var environment = _configuration[HostDefaults.EnvironmentKey];

        configureDelegate(_configuration);

        // 与环境相关的三个配置不允许改变
        Validate(applicationName, HostDefaults.ApplicationKey, "Application name cannot be changed.");
        Validate(contentRoot, HostDefaults.ContentRootKey, "Content root cannot be changed.");
        Validate(environment, HostDefaults.EnvironmentKey, "Environment name cannot be changed.");

        return this;

        void Validate(string previousValue, string key, string message)
        
            if (!string.Equals(previousValue, _configuration[key], StringComparison.OrdinalIgnoreCase))
            
                throw new NotSupportedException(message);
            
        
    

    public IHostBuilder ConfigureServices(
        Action<HostBuilderContext, IServiceCollection> configureDelegate)
        => Configure(() => configureDelegate(_context, _services));

    public IHostBuilder UseServiceProviderFactory<TContainerBuilder>(
        IServiceProviderFactory<TContainerBuilder> factory)
        => Configure(() => _configureActions.Add(
        b => b.UseServiceProviderFactory(factory)));

    public IHostBuilder UseServiceProviderFactory<TContainerBuilder>(
        Func<HostBuilderContext, IServiceProviderFactory<TContainerBuilder>> factory)
        => Configure(() => _configureActions.Add(
        b => b.UseServiceProviderFactory(factory)));

    public IHostBuilder ConfigureContainer<TContainerBuilder>(
        Action<HostBuilderContext, TContainerBuilder> configureDelegate)
        => Configure(() => _configureActions.Add(
        b => b.ConfigureContainer(configureDelegate)));

    private IHostBuilder Configure(Action configure)
    
        configure();
        return this;
    

    internal void Apply(IHostBuilder hostBuilder) 
        => _configureActions.ForEach(op => op(hostBuilder));

WebApplicationBuilder对象一旦被创建出来后,针对承载环境的配置是不能改变的,所以ConfigureHostBuilder的ConfigureHostConfiguration方法针对此添加了相应的验证。两个UseServiceProviderFactory方法和ConfigureContainer方法针对依赖注入容器的设置最终需要应用到IHostBuilder对象上,所以我们将方法中提供的委托对象利用configureActions字段存起来,并最终利用Apply方法应用到指定的IHostBuilder对象上。

3. ConfigureWebHostBuilder

ConfigureWebHostBuilder同样是在应用了BootstrapHostBuilder提供的初始化设置后创建的,创建该对象时能够提供WebHostBuilderContext上下文和承载配置和服务注册的ConfigurationManager和IServiceCollection对象。由于IWebHostBuilder接口定义的方法只涉及服务注册和针对配置的设置,所以方法提供的委托对象可以直接应用到这两个对象上。

public class ConfigureWebHostBuilder : IWebHostBuilder, ISupportsStartup

    private readonly WebHostBuilderContext _builderContext;
    private readonly IServiceCollection _services;
    private readonly ConfigurationManager _configuration;

    public ConfigureWebHostBuilder(
        WebHostBuilderContext builderContext, 
        ConfigurationManager configuration, 
        IServiceCollection services)
    
        _builderContext = builderContext;
        _services = services;
        _configuration = configuration;
    

    public IWebHost Build() 
        => throw new NotImplementedException();
    public IWebHostBuilder ConfigureAppConfiguration(
        Action<WebHostBuilderContext, IConfigurationBuilder> configureDelegate) 
        => Configure(() => configureDelegate(_builderContext, _configuration));
    public IWebHostBuilder ConfigureServices(
        Action<IServiceCollection> configureServices) 
        => Configure(() => configureServices(_services));
    public IWebHostBuilder ConfigureServices(
        Action<WebHostBuilderContext, IServiceCollection> configureServices) 
        => Configure(() => configureServices(_builderContext, _services));
    public string? GetSetting(string key) 
        => _configuration[key];
    public IWebHostBuilder UseSetting(string key, string? value) 
        => Configure(() => _configuration[key] = value);

    IWebHostBuilder ISupportsStartup.UseStartup(Type startupType) 
        => throw new NotImplementedException();
    IWebHostBuilder ISupportsStartup.UseStartup<TStartup>(
        Func<WebHostBuilderContext, TStartup> startupFactory) 
        => throw new NotImplementedException();
    IWebHostBuilder ISupportsStartup.Configure(
        Action<IApplicationBuilder> configure) 
        => throw new NotImplementedException();
    IWebHostBuilder ISupportsStartup.Configure(
        Action<WebHostBuilderContext, IApplicationBuilder> configure) 
        => throw new NotImplementedException();

    private IWebHostBuilder Configure(Action configure)
    
        configure();
        return this;
    

我们在前面说过,传统承载方式将初始化操作定义在注册的Startup类型的编程方式在Minima API中已经不再被支持了,所以WebApplicationBuilder本不该实现ISupportsStartup接口,但是我们希望用户在采用这种编程方式时得到显式的提醒,所以依然让它实现该接口,并在实现的方法中抛出NotImplementedException类型的异常。

4. WebApplicationBuilder

如下的代码片段模拟了WebApplicationBuilder针对WebApplication的构建。它的构造函数会创建一个BootstrapHostBuilder对象,调用它的ConfigureDefaults和ConfigureWebHostDefaults扩展方法将初始化设置收集起来。ConfigureWebHostDefaults方法会利用提供的Action<IWebHostBuilder>委托进行中间件的注册,由于中间件的注册被转移到WebApplication对象上,并且它提供了一个BuildRequestDelegate方法返回由注册中间件组成的管道,所以在这里只需调用构建的WebApplication对象(通过_application字段表示,虽然此时尚未创建,但是中间件真正被注册时会被创建出来)的这个方法,并将返回的RequestDelegate对象作为参数调用IApplicationBuilder接口的Run方法将中间件管道注册为请求处理器。

public class WebApplicationBuilder

    private readonly HostBuilder _hostBuilder 
        = new HostBuilder();
    private WebApplication _application;

    public ConfigurationManager Configuration  get;   
        = new ConfigurationManager();
    public IServiceCollection Services  get;   
        = new ServiceCollection();
    public IWebHostEnvironment Environment  get; 
    public ConfigureHostBuilder Host  get; 
    public ConfigureWebHostBuilder WebHost  get; 
    public ILoggingBuilder Logging  get; 

    public WebApplicationBuilder(WebApplicationOptions options)
    
        //创建BootstrapHostBuilder并利用它收集初始化过程中设置的配置、服务和针对依赖注入容器的设置
        var args = options.Args;
        var bootstrap = new BootstrapHostBuilder();
        bootstrap
            .ConfigureDefaults(null)
            .ConfigureWebHostDefaults(webHostBuilder => webHostBuilder 
                .Configure(app => app.Run(_application.BuildRequestDelegate())))
            .ConfigureHostConfiguration(config => 
                // 添加命令行配置源
                if (args?.Any() == true)
                
                    config.AddCommandLine(args);
                

                // 将WebApplicationOptions配置选项转移到配置中
                Dictionary<string, string>? settings = null;
                if (options.EnvironmentName is not null)  
                   (settings ??= new())[HostDefaults.EnvironmentKey]   
                   = options.EnvironmentName;
                if (options.ApplicationName is not null)   
                   (settings ??= new())[HostDefaults.ApplicationKey]   
                   = options.ApplicationName;
                if (options.ContentRootPath is not null)   
                   (settings ??= new())[HostDefaults.ContentRootKey]   
                   = options.ContentRootPath;
                if (options.WebRootPath is not null)   
                   (settings ??= new())[WebHostDefaults.WebRootKey]   
                   = options.EnvironmentName;
                if (settings != null)
                
                    config.AddInMemoryCollection(settings);
                
            );

        // 将BootstrapHostBuilder收集到配置和服务转移到Configuration和Services上
        // 将应用到BootstrapHostBuilder上针对依赖注入溶质的设置转移到_hostBuilder上
        // 得到BuilderContext上下文
        bootstrap.Apply(_hostBuilder, Configuration,  
           Services, out var builderContext);

        // 如果提供了命令行参数,在Configuration上添加对应配置源
        if (options.Args?.Any() == true)
        
            Configuration.AddCommandLine(options.Args);
        

        // 构建WebHostBuilderContext上下文
        // 初始化Host、WebHost和Logging属性
        var webHostContext = (WebHostBuilderContext)builderContext  
           .Properties[typeof(WebHostBuilderContext)];
        Environment = webHostContext.HostingEnvironment;
        Host = new ConfigureHostBuilder( 
           builderContext, Configuration, Services);
        WebHost = new ConfigureWebHostBuilder( 
           webHostContext, Configuration, Services);
        Logging = new LogginigBuilder(Services);
    

    public WebApplication Build()
    
        // 将ConfigurationManager的配置转移到_hostBuilder
        _hostBuilder.ConfigureAppConfiguration(builder =>
        
            builder.AddConfiguration(Configuration);
            foreach (var kv in  
               ((IConfigurationBuilder)Configuration).Properties)
            
                builder.Properties[kv.Key] = kv.Value;
            
        );

        // 将添加的服务注册转移到_hostBuilder
        _hostBuilder.ConfigureServices((_, services) =>
        
            foreach (var service in Services)
            
                services.Add(service);
            
        );

        // 将应用到Host属性上的设置转移到_hostBuilder
        Host.Apply(_hostBuilder);

        // 利用_hostBuilder构建的IHost对象创建WebApplication
        return _application = new WebApplication(_hostBuilder.Build());
    

接下来BootstrapHostBuilder的ConfigureHostConfiguration方法被调用,我们利用它将提供的WebApplicationOptions配置选项转移到BootstrapHostBuilder针对宿主的配置上。针对IHostBuilder初始化设置应用到BootstrapHostBuilder对象上之后,我们调用其Apply方法将这些设置分别转移到承载服务注册和配置的IServiceCollection和ConfigurationManager对象,以及封装的HostBuilder对象上。Apply方法利用输出参数提供了HostBuilderContext上下文,我们进一步从中提取出WebHostBuilderContext上下文(GenericWebHostBuilder会将构建的WebHostBuilderContext上下文置于HostBuilderContext对象的属性字典中)。我们利用这个上下文将ConfigureHostBuilder和ConfigureWebHostBuilder对象创建出来,并作为Host和WebHost属性。用于对日志做进一步设置的Logging属性也在这里被初始化,返回的LoggingBuilder对象仅仅是对IServiceCollection对象的简单封装而已。

构建WebApplication对象的Build方法分别调用ConfigureAppConfiguration和ConfigureServices方法将ConfigurationManager和IServiceCollection对象承载的配置和服务注册转移到HostBuilder对象上。它接下来提取出Host属性返回的ConfigureHostBuilder对象,并调用其Apply方法将应用在该对象上针对依赖注入容器的设置转移到HostBuilder对象上。至此所有的设置全部转移到了HostBuilder对象上,我们调用其Build方法构建出对应的IHost对象后,最后利用后者将代码承载应用的WebApplication对象构建出来。我们将这个对象赋值到_application字段上,前面调用ConfigureWebHostDefaults扩展方法提供的委托会将它的BuildRequestDelegate方法构建的中间件管道作为请求处理器。

四、 工厂方法

代表承载应用的WebApplication对象由WebApplicationBuilder构建的,但是我们一般不会通过调用构造函数的方式来创建WebApplicationBuilder对象,这有违“面向接口”编程的原则,所以我们都会使用WebApplication类型提供的静态工厂方法来创建它。WebApplication除了提供了三个用于创建WebApplicationBuilder的CreateBuilder方法重载,还提供了一个直接创建WebApplication对象的Create方法。

public sealed class WebApplication

    public static WebApplicationBuilder CreateBuilder() 
        => new WebApplicationBuilder(new WebApplicationOptions());

    public static WebApplicationBuilder CreateBuilder(string[] args)
    
        var options = new WebApplicationOptions();
        options.Args = args;
        return new WebApplicationBuilder(options);
    

    public static WebApplicationBuilder CreateBuilder(WebApplicationOptions options) 
        => new WebApplicationBuilder(options, null);

    public static WebApplication Create(string[]? args = null)
    
        var options = new WebApplicationOptions();
        options.Args = args;
        return new WebApplicationBuilder(options).Build();
    

本节内容通过针对WebApplication和WebApplicationBuilder这两个类型的实现模拟来讲解Minimal API的实现原理。一方面为了让讲解更加清晰,另一方面也出于篇幅的限制,不得不省去很多细枝末节的内容,但是设计思想和实现原理别无二致。上面提供的源代码也不是伪代码,如下所示的就是在“模拟的Minimal API”构建的ASP.NET Core应用,它是可以正常运行的。如果读者朋友们对真实的实现感兴趣,可以将它作为一个“向导”去探寻“真实的Minimal API”。

var app = App.WebApplication.Create();
app.Run(httpContext => httpContext.Response.WriteAsync("Hello World!"));
app.Run();

asp.netcore6框架揭秘实例演示[01]:编程初体验

本篇提供的20个简单的演示实例基本涵盖了ASP.NETCore6基本的编程模式,我们不仅会利用它们来演示针对控制台、API、MVC、gRPC应用的构建与编程,还会演示Dapr在.NET6中的应用。除此之外,这20个实例还涵盖了针对依赖注... 查看详情

netcore6揭秘怎么样

...的后台服务...2022年3月16日这篇文章主要介绍了ASP.NETCore6框架揭秘实例演示之如何承载你的后台服6框架揭秘实例演示之如何承载你的后台服务...2022年3月16日这篇文章主要介绍了ASP.NETCore6框架揭秘实例演示之如何承载... 查看详情

《asp.netcore6框架揭秘》实例演示[20]:“数据保护”框架基于文件的密钥存储...

...对密钥的创建、撤销和回收的实现原理。[本文节选《ASP.NETCore6框架揭秘 查看详情

《asp.netcore6框架揭秘》实例演示[13]:日志的基本编程模式

《ASP.NETCore6框架揭秘实例演示[11]:诊断跟踪的几种基本编程方式》介绍了四种常用的诊断日志框架。其实除了微软提供的这些日志框架,还有很多第三方日志框架可供我们选择,比如Log4Net、NLog和Serilog等。虽然这些... 查看详情

《asp.netcore6框架揭秘》实例演示[31]:路由高阶用法

ASP.NET的路由是通过EndpointRoutingMiddleware和EndpointMiddleware这两个中间件协作完成的,它们在ASP.NET平台上具有举足轻重的地位,MVC和gRPC框架,Dapr的Actor和发布订阅编程模式都建立在路由系统之上。MinimalAPI更是将提升到了... 查看详情

《asp.netcore6框架揭秘》实例演示[10]:options基本编程模式

...#xff0c;这篇文章演示几种典型的编程模式。[本文节选《ASP.NETCore6框架揭秘》第6章][601]将配置绑定为Options对象(源代码&#x 查看详情

《asp.netcore6框架揭秘》实例演示[04]:自定义依赖注入框架

ASP.NETCore框架建立在一个依赖注入框架之上,已注入的方式消费服务已经成为了ASP.NETCore基本的编程模式。为了使读者能够更好地理解原生的注入框架框架,我按照类似的设计创建了一个简易版本的依赖注入框架,并... 查看详情

《asp.netcore6框架揭秘》实例演示[22]:如何承载你的后台服务[补充]

...Core应用最终也体现为这样一个承载服务。[本文节选《ASP.NETCore6框架揭秘》第14章][S1407]利用IHostAppl 查看详情

《asp.netcore6框架揭秘》实例演示[19]:数据加解密与哈希

数据保护(DataProtection)框架旨在解决数据在传输与持久化存储过程中的一致性(Integrity)和机密性(confidentiality)问题,前者用于检验接收到的数据是否经过篡改,后者通过对原始的数据进行加密... 查看详情

asp.netcore6框架揭秘实例演示[30]:利用路由开发restapi

借助路由系统提供的请求URL模式与对应终结点之间的映射关系,我们可以将具有相同URL模式的请求分发给与之匹配的终结点进行处理。ASP.NET的路由是通过EndpointRoutingMiddleware和EndpointMiddleware这两个中间件协作完成的,它们... 查看详情

《asp.netcore6框架揭秘》实例演示[28]:自定义一个服务器

作为ASP.NETCore请求处理管道的“龙头”的服务器负责监听和接收请求并最终完成对请求的响应。它将原始的请求上下文描述为相应的特性(Feature),并以此将HttpContext上下文创建出来,中间件针对HttpContext上下文的... 查看详情

《asp.netcore6框架揭秘》实例演示[34]:缓存整个响应内容

我们利用ASP.NET开发的大部分API都是为了对外提供资源,对于不易变化的资源内容,针对某个维度对其实施缓存可以很好地提供应用的性能。《内存缓存与分布式缓存的使用》介绍的两种缓存框架(本地内存缓存和分... 查看详情

《asp.netcore6框架揭秘》实例演示[25]:配置与承载环境的应用

与服务注册一样,针对配置的设置同样可以采用三种不同的编程模式。第一种是利用WebApplicationBuilder的Host属性返回的IHostBuilder对象,它可以帮助我们设置面向宿主和应用的配置。IWebHostBuilder接口上面同样提供了一系列用... 查看详情

《asp.netcore6框架揭秘》实例演示[26]:跟踪应用接收的每一次请求

很多人可能对ASP.NETCore框架自身记录的诊断日志并不关心,其实这些日志对纠错排错和性能监控提供了很有用的信息。如果需要创建一个APM(ApplicationPerformanceManagement)系统来监控ASP.NETCore应用处理请求的性能及出现的... 查看详情

asp.netcore6框架揭秘实例演示[29]:搭建文件服务器

通过HTTP请求获取的Web资源很多都来源于存储在服务器磁盘上的静态文件。对于ASP.NET应用来说,如果将静态文件存储到约定的目录下,绝大部分文件类型都是可以通过Web的形式对外发布的。“Microsoft.AspNetCore.StaticFiles”这... 查看详情

asp.netcore6框架揭秘实例演示[32]:错误页面的n种呈现方式

由于ASP.NET是一个同时处理多个请求的Web应用框架,所以在处理某个请求过程中出现异常并不会导致整个应用的中止。出于安全方面的考量,为了避免敏感信息外泄,客户端在默认情况下并不会得到详细的出错信息,这无疑会在... 查看详情

asp.netcore6框架揭秘实例演示[21]:如何承载你的后台服务(代码片段)

借助.NET提供的服务承载(Hosting)系统,我们可以将一个或者多个长时间运行的后台服务寄宿或者承载我们创建的应用中。任何需要在后台长时间运行的操作都可以定义成标准化的服务并利用该系统来承载,ASP.NETCore应用最终也... 查看详情

asp.netcore6框架揭秘实例演示[22]:如何承载你的后台服务[补充](代码片段)

借助.NET提供的服务承载(Hosting)系统,我们可以将一个或者多个长时间运行的后台服务寄宿或者承载我们创建的应用中。任何需要在后台长时间运行的操作都可以定义成标准化的服务并利用该系统来承载,ASP.NETCore应用最终也... 查看详情