钱行慕

导航

ASP.NET Core中的Startup

此为系列文章,对MSDN ASP.NET Core 的官方文档进行系统学习与翻译。其中或许会添加本人对 ASP.NET Core 的浅显理解

          Startup类配置了ASP.NET Core所需要的服务以及app的请求管道。

 Startup类

         ASP.NET Core使用了一个Startup类,按照惯例,其被命名为Startup。Startup类主要包含:

  •          可选的包含了一个ConfigureServices类用以配置应用程序的服务。服务是给app提供功能的可复用组件。服务在ConfigureServices中被注册并且通过DI或者  ApplicationServices在整个app中被消费。
  •          包含一个Configure用来创建app的请求处理管道。

       当app启动时,ConfigureServicesConfigure会被ASP.NET Core运行时所调用:

      

public class Startup
{
    public Startup(IConfiguration configuration)
    {
        Configuration = configuration;
    }

    public IConfiguration Configuration { get; }

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddRazorPages();
    }

    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }
        else
        {
            app.UseExceptionHandler("/Error");
            app.UseHsts();
        }

        app.UseHttpsRedirection();
        app.UseStaticFiles();

        app.UseRouting();

        app.UseAuthorization();

        app.UseEndpoints(endpoints =>
        {
            endpoints.MapRazorPages();
        });
    }
}

         以上的示例代码来源于Razor页面程序,MVC版本的Startup是类似的。

         当app建立宿主时候,便会指定Startup方法。通常我们是在宿主建造器中通过调用WebHostBuilderExtensions.UseStartup<TStartup> 来指定一个Startup的。如下所示:

 

public class Program
{
    public static void Main(string[] args)
    {
        CreateHostBuilder(args).Build().Run();
    }

    public static IHostBuilder CreateHostBuilder(string[] args) =>
        Host.CreateDefaultBuilder(args)
            .ConfigureWebHostDefaults(webBuilder =>
            {
                webBuilder.UseStartup<Startup>();
            });
}

         宿主提供器服务对于Startup的构造函数来说是可用的。而app通过ConfigureServices来添加额外的服务。然后,宿主提供器服务以及额外添加的服务在Configure中以及整个app中都是可用的。

        当使用Generic Host (IHostBuilder)时候,只有如下的服务类型可以被注入到Startup构造函数中: 

 

public class Startup
{
    private readonly IWebHostEnvironment _env;

    public Startup(IConfiguration configuration, IWebHostEnvironment env)
    {
        Configuration = configuration;
        _env = env;
    }

    public IConfiguration Configuration { get; }

    public void ConfigureServices(IServiceCollection services)
    {
        if (_env.IsDevelopment())
        {
        }
        else
        {
        }
    }
}

         大部分服务直到调用Configure方法之后才可用使用。

 多个Startup

        当app为了不同的环境定义了不同的Startup时(例如StartupDevelopment),在运行时ASP.NET Core会选择合适的Startup类。名称后缀匹配当前环境的类会被优先选择。如果app运行在Development环境并且都包含了一个Startup类和一个StartupDevelopment类,那么StartupDevelopment类会被使用。更多信息,请参考Use multiple environments

       更多关于宿主的信息,请参考The host。关于在startup中处理错误的信息,请参考Startup exception handling

ConfigureServices方法

       ConfigureServices方法是:

  •                可选的
  •                在Configure方法对app的服务进行配置之前被宿主调用
  •                按照惯例,在这儿配置选项被设置

      在Startup方法被调用之前,宿主可能会配置一些服务,更多信息,请参考The host

      对于那些需要实质性安装的特性来说,我们可以使用IServiceCollectionAdd{Service}扩展方法。比如AddDbContext,AddDefaultIdentity,AddEntityFrameworkStores 以及 AddRazorPages:

   

public class Startup
{
    public Startup(IConfiguration configuration)
    {
        Configuration = configuration;
    }

    public IConfiguration Configuration { get; }

    public void ConfigureServices(IServiceCollection services)
    {

        services.AddDbContext<ApplicationDbContext>(options =>
            options.UseSqlServer(
                Configuration.GetConnectionString("DefaultConnection")));
        services.AddDefaultIdentity<IdentityUser>(
            options => options.SignIn.RequireConfirmedAccount = true)
            .AddEntityFrameworkStores<ApplicationDbContext>();

        services.AddRazorPages();
    }

         将服务添加进服务容器使得它们在整个app以及Configure方法里都是可用的。那些服务是通过dependency injection 或从ApplicationServices里进行解析。

Configure方法

      Configure方法用来指定app如何响应一个HTTP 请求 。通过给一个IApplicationBuilder实例添加中间价组件的方式可用对一个请求管道进行配置。IApplicationBuilder对于Configure方法来说是可用的,但其并不在服务容器中进行注册。宿主创建了一个IApplicationBuilder并把它直接传递给Configure方法。

     ASP.NET Core 模板 配置了请求响应管道以支持:          

public class Startup
{
    public Startup(IConfiguration configuration)
    {
        Configuration = configuration;
    }

    public IConfiguration Configuration { get; }

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddRazorPages();
    }

    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }
        else
        {
            app.UseExceptionHandler("/Error");
            app.UseHsts();
        }

        app.UseHttpsRedirection();
        app.UseStaticFiles();

        app.UseRouting();

        app.UseAuthorization();

        app.UseEndpoints(endpoints =>
        {
            endpoints.MapRazorPages();
        });
    }
}

       前面的示例是用于Razor Pages,MVC版本类似。

       每一个Use扩展方法都会给请求处理管道添加一个或者多个中间件组件。比如UseStaticFiles配置了中间件用来服务static files

       请求管道中的每一个中间件组件都负责激活下一个组件或者将链条终结,如果正是这样希望的话。

       其他服务,比如IWebHostEnvironmentILoggerFactory,或者ConfigureServices中定义的任何东西,都可以在Configure方法签名中进行指定。如果这些服务是可用的,那么它们便会被注入到Configure方法。

        更多关于如何使用IApplicationBuilder以及中间件处理顺序的信息,请参考ASP.NET Core Middleware

不使用Startup来配置服务

       为了不使用Startup来配置服务以及请求处理管道,我们可以在宿主构造器中调用ConfigureServicesConfigure的方便方法。对ConfigureServices方法的多次调用可用追加在另一个之后。如果存在多个Configure方法调用,那么最后Configure方法调用会被使用。

public class Program
{
    public static void Main(string[] args)
    {
        CreateHostBuilder(args).Build().Run();
    }

    public static IHostBuilder CreateHostBuilder(string[] args) =>
        Host.CreateDefaultBuilder(args)
            .ConfigureAppConfiguration((hostingContext, config) =>
            {
            })
            .ConfigureWebHostDefaults(webBuilder =>
            {
                webBuilder.ConfigureServices(services =>
                {
                    services.AddControllersWithViews();
                })
                .Configure(app =>
                {
                    var loggerFactory = app.ApplicationServices
                        .GetRequiredService<ILoggerFactory>();
                    var logger = loggerFactory.CreateLogger<Program>();
                    var env = app.ApplicationServices.GetRequiredService<IWebHostEnvironment>();
                    var config = app.ApplicationServices.GetRequiredService<IConfiguration>();

                    logger.LogInformation("Logged in Configure");

                    if (env.IsDevelopment())
                    {
                        app.UseDeveloperExceptionPage();
                    }
                    else
                    {
                        app.UseExceptionHandler("/Home/Error");
                        app.UseHsts();
                    }

                    var configValue = config["MyConfigKey"];
                });
            });
        });
}

 使用Startup过滤器扩展Startup

       使用IStartupFilter

  •               可以在app配置中间件管道的开始或者结束配置中间件,而不用显示的调用Use{Middleware}方法。ASP.NET Core使用IStartupFilter来给管道的开始添加默认值,而不用使app的作者显式的注册默认中间件。IStartupFilter允许另一种不同的组件调用,其有益于app的作者。
  •               创建了Configure方法的管道。IStartupFilter.Configure 可以设置中间件在 类库添加的中间件之前或者之后运行。

      IStartupFilter实现了Configure,其接收并返回一个Action<IApplicationBuilder>。IApplicationBuilder定义了一个类来配置一个app的请求处理管道。更多信息,请参考Create a middleware pipeline with IApplicationBuilder

      每一个IStartupFilter都会在请求管道中添加一个或者多个中间件。过滤器以它们添加到服务容器中的顺序被激活。过滤器会在其将控制器传递给下一个过滤器之前或者之后添加中间件,因而它们会被追加到管道的开始或者末尾 。

      以下示例演示了如何使用一个IStartupFilter来注册一个中间件。此中间件RequestSetOptionsMiddleware从查询字符串中设置了一个选项值。

    

public class RequestSetOptionsMiddleware
{
    private readonly RequestDelegate _next;

    public RequestSetOptionsMiddleware( RequestDelegate next )
    {
        _next = next;
    }

    // Test with https://localhost:5001/Privacy/?option=Hello
    public async Task Invoke(HttpContext httpContext)
    {
        var option = httpContext.Request.Query["option"];

        if (!string.IsNullOrWhiteSpace(option))
        {
            httpContext.Items["option"] = WebUtility.HtmlEncode(option);
        }

        await _next(httpContext);
    }
}

         中间件RequestSetOptionsMiddlewareRequestSetOptionsStartupFilter类中进行配置:

public class RequestSetOptionsStartupFilter : IStartupFilter
{
    public Action<IApplicationBuilder> Configure(Action<IApplicationBuilder> next)
    {
        return builder =>
        {
            builder.UseMiddleware<RequestSetOptionsMiddleware>();
            next(builder);
        };
    }
}

           IStartupFilterConfigureServices中的服务容器中进行注册:

   

public class Program
{
    public static void Main(string[] args)
    {
        CreateHostBuilder(args).Build().Run();
    }

    public static IHostBuilder CreateHostBuilder(string[] args) =>
        Host.CreateDefaultBuilder(args)
           .ConfigureAppConfiguration((hostingContext, config) =>
           {
           })
         .ConfigureWebHostDefaults(webBuilder =>
         {
             webBuilder.UseStartup<Startup>();
         })
        .ConfigureServices(services =>
        {
            services.AddTransient<IStartupFilter,
                      RequestSetOptionsStartupFilter>();
        });
}

        当一个option查询字符串被提供时,在ASP.NET Core中间件呈现响应之前,中间件会对这个值进行处理。

       IStartupFilter的注册顺序决定了中间件的执行顺序:

  •               多个IStartupFilter的实现会与一个相同的对象交互,如果顺序是重要的,我们必须对IStartupFilter的注册进行排序,以使得这些中间件可以按本应的顺序来执行。
  •              库可能会使用一个或者多个在其他 由IStartupFilter注册的app中间件的开始或者结束运行的IStartupFilter实现来添加中间件。为了在一个由库的IStartupFilter添加的中间件之前激活一个IStartupFilter中间件,我们必须:
  1.           将服务注册的位置放在库被添加到服务容器之前
  2.           为了之后激活,将服务注册的位置放在库被添加之后

在 Startup中从外部程序集加载配置

       一个IHostingStartup实现允许我们在startup中从一个外部程序集中对app进行增强。更多信息,请参考Use hosting startup assemblies in ASP.NET Core

额外资源

    

posted on 2020-02-27 11:18  钱行慕  阅读(719)  评论(2编辑  收藏  举报